diff --git a/pkg/cron/cron_test.go b/pkg/cron/cron_test.go deleted file mode 100644 index f949a58..0000000 --- a/pkg/cron/cron_test.go +++ /dev/null @@ -1,25 +0,0 @@ -package cron - -import ( - "sync" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestCron(t *testing.T) { - var wg sync.WaitGroup - now := time.Now() - next := now.Add(time.Second * 4) - wg.Add(1) - cron := New() - err := cron.AddJob("test", "@every 3s", func() { - wg.Done() - }) - require.NoError(t, err) - wg.Wait() - now2 := time.Now() - assert.True(t, now2.After(now) && now2.Before(next)) -} diff --git a/pkg/docker/fake/cli.go b/pkg/docker/fake/cli.go index 00e6559..8ac8785 100644 --- a/pkg/docker/fake/cli.go +++ b/pkg/docker/fake/cli.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "sync" "time" "github.com/docker/docker/api/types" @@ -15,10 +16,13 @@ import ( ) type Client struct { + mu sync.Mutex containers []types.Container } func (f *Client) RunContainer(ctx context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, containerName string) (id string, err error) { + f.mu.Lock() + defer f.mu.Unlock() for _, ct := range f.containers { if ct.Names[0] == containerName { return "", errdefs.Conflict(errors.New("container already exists")) @@ -40,6 +44,8 @@ func (f *Client) PullImage(ctx context.Context, image string) error { } func (f *Client) WaitContainer(ctx context.Context, id string) (int, error) { + f.mu.Lock() + defer f.mu.Unlock() for i, ct := range f.containers { if ct.ID == id { time.Sleep(5 * time.Second) @@ -51,6 +57,8 @@ func (f *Client) WaitContainer(ctx context.Context, id string) (int, error) { } func (f *Client) RemoveContainerWithTimeout(id string, timeout time.Duration) error { + f.mu.Lock() + defer f.mu.Unlock() cts := make([]types.Container, 0, len(f.containers)) for _, ct := range f.containers { if ct.ID != id { @@ -62,6 +70,8 @@ func (f *Client) RemoveContainerWithTimeout(id string, timeout time.Duration) er } func (f *Client) ListContainersWithTimeout(running bool, timeout time.Duration) ([]types.Container, error) { + f.mu.Lock() + defer f.mu.Unlock() return f.containers, nil } diff --git a/pkg/server/main.go b/pkg/server/main.go index 10a92bc..556faa3 100644 --- a/pkg/server/main.go +++ b/pkg/server/main.go @@ -55,8 +55,10 @@ func New(configPath string) (*Server, error) { } func NewWithConfig(cfg Config) (*Server, error) { + // TODO: enforce shared cache mode? db, err := gorm.Open(sqlite.Open(cfg.DbURL), &gorm.Config{ - QueryFields: true, + QueryFields: true, + SkipDefaultTransaction: true, }) if err != nil { return nil, fmt.Errorf("open db: %w", err) diff --git a/pkg/server/main_test.go b/pkg/server/main_test.go index 33e0c08..3a38494 100644 --- a/pkg/server/main_test.go +++ b/pkg/server/main_test.go @@ -56,9 +56,15 @@ func NewTestEnv(t *testing.T) *TestEnv { _ = os.Remove(dbFile.Name()) }) db, err := gorm.Open(sqlite.Open(dbFile.Name()), &gorm.Config{ - QueryFields: true, + QueryFields: true, + SkipDefaultTransaction: true, }) require.NoError(t, err) + sqlDB, err := db.DB() + require.NoError(t, err) + // To resolve the "database is locked" error. + // See also https://github.com/mattn/go-sqlite3/issues/209 + sqlDB.SetMaxOpenConns(1) require.NoError(t, model.AutoMigrate(db)) s := &Server{ diff --git a/pkg/server/utils.go b/pkg/server/utils.go index a6337a4..b6d067c 100644 --- a/pkg/server/utils.go +++ b/pkg/server/utils.go @@ -97,7 +97,6 @@ func (s *Server) waitForSync(name, ctID, storageDir string) { code = -2 } } - s.syncingContainers.Delete(name) err = s.dockerCli.RemoveContainerWithTimeout(ctID, time.Second*20) if err != nil { l.Error("Fail to remove container", slogErrAttr(err)) @@ -119,6 +118,10 @@ func (s *Server) waitForSync(name, ctID, storageDir string) { l.Error("Fail to update RepoMeta", slogErrAttr(err)) } + // NOTE: Only change status after RepoMeta is updated, b/c we need to determine + // if synchronization is completed in unit test, and then verify RepoMeta. + s.syncingContainers.Delete(name) + if len(s.config.PostSync) == 0 { return }