Skip to content

Commit 0ff26be

Browse files
taokyiBug
andauthored
feat: implement SyncTimeout (#33)
* feat: implement SyncTimeOut * Wrap s.context() in new context * feat: use type time.Duration for SyncTimeout * test: add testing for config parsing * test: add test for waitForSync() * test: modify testcase to use background context with cancel * chore: set sync_timeout example value in dist/daemon.toml * chore: fmt: Use %s for time.Duration * chore: Use ctx from param --------- Co-authored-by: iBug <git@ibugone.com>
1 parent 3b2eada commit 0ff26be

File tree

8 files changed

+159
-10
lines changed

8 files changed

+159
-10
lines changed

cmd/yukid/README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,11 @@ repo_config_dir = ["/path/to/config-dir"]
6767
## 设置更新用到的 docker images 的频率
6868
## 格式为 crontab
6969
#images_upgrade_interval = "@every 1h"
70+
71+
## 同步超时时间,如果超过了这个时间,同步容器会被强制停止
72+
## 支持使用 time.ParseDuration() 支持的时间格式,诸如 "10m", "1h" 等
73+
## 如果为 0 的话则不会超时。注意修改的配置仅对新启动的同步容器生效
74+
#sync_timeout = "48h"
7075
```
7176

7277
### Repo Configuration

dist/daemon.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,5 @@ repo_config_dir = "/path/to/config-dir"
3232
#post_sync = ["/path/to/the/program"]
3333

3434
#images_upgrade_interval = "@every 1h"
35+
36+
#sync_timeout = "48h"

pkg/server/config.go

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package server
33
import (
44
"fmt"
55
"os"
6+
"time"
67

78
"github.com/sirupsen/logrus"
89
"github.com/spf13/viper"
@@ -19,15 +20,16 @@ type AppConfig struct {
1920
FileSystem string `mapstructure:"fs,omitempty" validate:"omitempty,eq=xfs|eq=zfs|eq=default"`
2021
DockerEndpoint string `mapstructure:"docker_endpoint,omitempty" validate:"omitempty,unix_addr|tcp_addr"`
2122

22-
Owner string `mapstructure:"owner,omitempty" validate:"-"`
23-
LogDir string `mapstructure:"log_dir,omitempty" validate:"-"`
24-
RepoConfigDir []string `mapstructure:"repo_config_dir,omitempty" validate:"required"`
25-
LogLevel string `mapstructure:"log_level,omitempty" validate:"omitempty,eq=debug|eq=info|eq=warn|eq=error"`
26-
ListenAddr string `mapstructure:"listen_addr,omitempty" validate:"omitempty,hostport"`
27-
BindIP string `mapstructure:"bind_ip,omitempty" validate:"omitempty,ip"`
28-
NamePrefix string `mapstructure:"name_prefix,omitempty" validate:"-"`
29-
PostSync []string `mapstructure:"post_sync,omitempty" validate:"-"`
30-
ImagesUpgradeInterval string `mapstructure:"images_upgrade_interval,omitempty" validate:"omitempty,cron"`
23+
Owner string `mapstructure:"owner,omitempty" validate:"-"`
24+
LogDir string `mapstructure:"log_dir,omitempty" validate:"-"`
25+
RepoConfigDir []string `mapstructure:"repo_config_dir,omitempty" validate:"required"`
26+
LogLevel string `mapstructure:"log_level,omitempty" validate:"omitempty,eq=debug|eq=info|eq=warn|eq=error"`
27+
ListenAddr string `mapstructure:"listen_addr,omitempty" validate:"omitempty,hostport"`
28+
BindIP string `mapstructure:"bind_ip,omitempty" validate:"omitempty,ip"`
29+
NamePrefix string `mapstructure:"name_prefix,omitempty" validate:"-"`
30+
PostSync []string `mapstructure:"post_sync,omitempty" validate:"-"`
31+
ImagesUpgradeInterval string `mapstructure:"images_upgrade_interval,omitempty" validate:"omitempty,cron"`
32+
SyncTimeout time.Duration `mapstructure:"sync_timeout,omitempty" validate:"omitempty,gte=0"`
3133
}
3234

3335
type Config struct {
@@ -41,6 +43,7 @@ type Config struct {
4143
NamePrefix string
4244
PostSync []string
4345
ImagesUpgradeInterval string
46+
SyncTimeout time.Duration
4447
}
4548

4649
var (
@@ -57,6 +60,7 @@ var (
5760
NamePrefix: "syncing-",
5861
LogLevel: logrus.InfoLevel,
5962
ImagesUpgradeInterval: "@every 1h",
63+
SyncTimeout: 0,
6064
}
6165
)
6266

@@ -87,6 +91,7 @@ func LoadConfig() (*Config, error) {
8791
NamePrefix: appCfg.NamePrefix,
8892
PostSync: appCfg.PostSync,
8993
ImagesUpgradeInterval: appCfg.ImagesUpgradeInterval,
94+
SyncTimeout: appCfg.SyncTimeout,
9095
}
9196

9297
switch appCfg.FileSystem {

pkg/server/config_test.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package server
2+
3+
import (
4+
"testing"
5+
"time"
6+
7+
"github.com/spf13/viper"
8+
)
9+
10+
func TestDefaultTimeoutConfig(t *testing.T) {
11+
viper.Reset()
12+
viper.SetConfigFile("../../dist/daemon.toml")
13+
_, err := LoadConfig()
14+
if err != nil {
15+
t.Fatal(err)
16+
}
17+
}
18+
19+
func TestSyncTimeoutConfig(t *testing.T) {
20+
viper.Reset()
21+
viper.SetConfigFile("../../test/fixtures/sync_timeout.toml")
22+
config, err := LoadConfig()
23+
if err != nil {
24+
t.Fatal(err)
25+
}
26+
if config.SyncTimeout != time.Second*15 {
27+
t.Fatalf("Expected SyncTimeout to be 15s, got %s", config.SyncTimeout)
28+
}
29+
}

pkg/server/main.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package server
22

33
import (
44
"context"
5+
"errors"
56
"fmt"
67
"io/ioutil"
78
"os"
@@ -396,8 +397,21 @@ func (s *Server) waitForSync(ct *api.Container) error {
396397
Name: ct.Labels["org.ustcmirror.name"],
397398
}
398399

399-
code, err := s.c.WaitContainer(s.context(), ct.ID)
400+
ctx := s.context()
401+
if s.config.SyncTimeout > 0 {
402+
var cancel context.CancelFunc
403+
ctx, cancel = context.WithTimeout(ctx, s.config.SyncTimeout)
404+
defer cancel()
405+
}
406+
407+
code, err := s.c.WaitContainer(ctx, ct.ID)
400408
if err != nil {
409+
if errors.Is(ctx.Err(), context.DeadlineExceeded) {
410+
// timeout, we should stop the container
411+
ctx, cancel := context.WithTimeout(s.context(), time.Second*30)
412+
_ = s.c.StopContainer(ctx, ct.ID)
413+
cancel()
414+
}
401415
return err
402416
}
403417

pkg/server/main_test.go

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package server
2+
3+
import (
4+
"context"
5+
"os"
6+
"testing"
7+
8+
"github.com/spf13/viper"
9+
"github.com/stretchr/testify/assert"
10+
"github.com/stretchr/testify/require"
11+
"github.com/ustclug/Yuki/pkg/api"
12+
"github.com/ustclug/Yuki/pkg/core"
13+
)
14+
15+
func getTestingServer(ctx context.Context, prefix string, name string, storageDir string) (*Server, error) {
16+
s, err := New()
17+
if err != nil {
18+
return nil, err
19+
}
20+
21+
defer os.RemoveAll("/tmp/log_yuki")
22+
defer os.RemoveAll("/tmp/config_yuki")
23+
24+
err = os.MkdirAll(storageDir, os.ModePerm)
25+
if err != nil {
26+
return nil, err
27+
}
28+
defer os.RemoveAll(storageDir)
29+
// remove existing repository and container
30+
_ = s.c.RemoveRepository(name)
31+
_ = s.c.RemoveContainer(ctx, prefix+name)
32+
33+
s.ctx = ctx
34+
go s.onPreSync()
35+
go s.onPostSync()
36+
37+
return s, nil
38+
}
39+
40+
func TestWaitForSync(t *testing.T) {
41+
as := assert.New(t)
42+
req := require.New(t)
43+
ctx, cancel := context.WithCancel(context.Background())
44+
defer cancel()
45+
46+
viper.Reset()
47+
viper.SetConfigFile("../../test/fixtures/sync_timeout.toml")
48+
49+
prefix := "syncing-"
50+
name := "yuki-test-sync-repo"
51+
d := "/tmp/" + name
52+
cycle := 10
53+
54+
s, err := getTestingServer(ctx, prefix, name, d)
55+
req.Nil(err)
56+
57+
err = s.c.AddRepository(api.Repository{
58+
Name: name,
59+
Interval: "1 * * * *",
60+
Image: "ustcmirror/test:latest",
61+
StorageDir: d,
62+
LogRotCycle: &cycle,
63+
Envs: map[string]string{"SLEEP_INFINITY": "1"},
64+
})
65+
as.Nil(err)
66+
67+
ct, err := s.c.Sync(ctx, core.SyncOptions{
68+
MountDir: false,
69+
Name: name,
70+
NamePrefix: prefix,
71+
})
72+
as.Nil(err)
73+
defer s.c.RemoveContainer(ctx, ct.ID)
74+
err = s.waitForSync(ct)
75+
if err != context.DeadlineExceeded {
76+
t.Fatalf("Expected error to be context.DeadlineExceeded, got %v", err)
77+
}
78+
}

test/fixtures/normal.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
fs = "default"
2+
3+
db_name = "test"
4+
5+
log_dir = "/tmp/log_yuki/"
6+
7+
repo_config_dir = "/tmp/config_yuki"

test/fixtures/sync_timeout.toml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
fs = "default"
2+
3+
db_name = "test"
4+
5+
log_dir = "/tmp/log_yuki/"
6+
7+
repo_config_dir = "/tmp/config_yuki"
8+
9+
sync_timeout = "15s"

0 commit comments

Comments
 (0)