镜像自地址
https://github.com/tuna/tunasync.git
已同步 2025-12-07 15:06:47 +00:00
比较提交
15 次代码提交
| 作者 | SHA1 | 提交日期 | |
|---|---|---|---|
|
|
b8edc1f714 | ||
|
|
001703a059 | ||
|
|
2bbd4afda8 | ||
|
|
e8e6ab6ed6 | ||
|
|
3fed3f1cf3 | ||
|
|
1491b6c42b | ||
|
|
7a9895350b | ||
|
|
95d6acb026 | ||
|
|
b132192448 | ||
|
|
91209cab60 | ||
|
|
1fb9f85862 | ||
|
|
d10387e40b | ||
|
|
5c01e3fa22 | ||
|
|
a44891d3e8 | ||
|
|
4d461bd172 |
@@ -32,7 +32,7 @@ const (
|
||||
userCfgFile = "$HOME/.config/tunasync/ctl.conf" // user-specific conf
|
||||
)
|
||||
|
||||
var logger = logging.MustGetLogger("tunasynctl-cmd")
|
||||
var logger = logging.MustGetLogger("tunasynctl")
|
||||
|
||||
var baseURL string
|
||||
var client *http.Client
|
||||
@@ -41,7 +41,7 @@ func initializeWrapper(handler cli.ActionFunc) cli.ActionFunc {
|
||||
return func(c *cli.Context) error {
|
||||
err := initialize(c)
|
||||
if err != nil {
|
||||
return cli.NewExitError("", 1)
|
||||
return cli.NewExitError(err.Error(), 1)
|
||||
}
|
||||
return handler(c)
|
||||
}
|
||||
@@ -55,8 +55,9 @@ type config struct {
|
||||
|
||||
func loadConfig(cfgFile string, cfg *config) error {
|
||||
if cfgFile != "" {
|
||||
logger.Infof("Loading config: %s", cfgFile)
|
||||
if _, err := toml.DecodeFile(cfgFile, cfg); err != nil {
|
||||
logger.Errorf(err.Error())
|
||||
// logger.Errorf(err.Error())
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -66,7 +67,7 @@ func loadConfig(cfgFile string, cfg *config) error {
|
||||
|
||||
func initialize(c *cli.Context) error {
|
||||
// init logger
|
||||
tunasync.InitLogger(c.Bool("verbose"), c.Bool("verbose"), false)
|
||||
tunasync.InitLogger(c.Bool("verbose"), c.Bool("debug"), false)
|
||||
|
||||
cfg := new(config)
|
||||
|
||||
@@ -76,14 +77,23 @@ func initialize(c *cli.Context) error {
|
||||
|
||||
// find config file and load config
|
||||
if _, err := os.Stat(systemCfgFile); err == nil {
|
||||
loadConfig(systemCfgFile, cfg)
|
||||
err = loadConfig(systemCfgFile, cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
fmt.Println(os.ExpandEnv(userCfgFile))
|
||||
logger.Debug("user config file: %s", os.ExpandEnv(userCfgFile))
|
||||
if _, err := os.Stat(os.ExpandEnv(userCfgFile)); err == nil {
|
||||
loadConfig(os.ExpandEnv(userCfgFile), cfg)
|
||||
err = loadConfig(os.ExpandEnv(userCfgFile), cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if c.String("config") != "" {
|
||||
loadConfig(c.String("config"), cfg)
|
||||
err := loadConfig(c.String("config"), cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// override config using the command-line arguments
|
||||
@@ -112,7 +122,7 @@ func initialize(c *cli.Context) error {
|
||||
client, err = tunasync.CreateHTTPClient(cfg.CACert)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Error initializing HTTP client: %s", err.Error())
|
||||
logger.Error(err.Error())
|
||||
// logger.Error(err.Error())
|
||||
return err
|
||||
|
||||
}
|
||||
@@ -135,7 +145,7 @@ func listWorkers(c *cli.Context) error {
|
||||
err.Error()),
|
||||
1)
|
||||
}
|
||||
fmt.Print(string(b))
|
||||
fmt.Println(string(b))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -167,14 +177,21 @@ func listJobs(c *cli.Context) error {
|
||||
_, err := tunasync.GetJSON(fmt.Sprintf("%s/workers/%s/jobs",
|
||||
baseURL, workerID), &workerJobs, client)
|
||||
if err != nil {
|
||||
logger.Errorf("Filed to correctly get jobs"+
|
||||
logger.Infof("Failed to correctly get jobs"+
|
||||
" for worker %s: %s", workerID, err.Error())
|
||||
}
|
||||
ans <- workerJobs
|
||||
}(workerID)
|
||||
}
|
||||
for range args {
|
||||
jobs = append(jobs, <-ans...)
|
||||
job := <-ans
|
||||
if job == nil {
|
||||
return cli.NewExitError(
|
||||
fmt.Sprintf("Failed to correctly get information "+
|
||||
"of jobs from at least one manager"),
|
||||
1)
|
||||
}
|
||||
jobs = append(jobs, job...)
|
||||
}
|
||||
genericJobs = jobs
|
||||
}
|
||||
@@ -182,10 +199,10 @@ func listJobs(c *cli.Context) error {
|
||||
b, err := json.MarshalIndent(genericJobs, "", " ")
|
||||
if err != nil {
|
||||
return cli.NewExitError(
|
||||
fmt.Sprintf("Error printing out informations: %s", err.Error()),
|
||||
fmt.Sprintf("Error printing out information: %s", err.Error()),
|
||||
1)
|
||||
}
|
||||
fmt.Printf(string(b))
|
||||
fmt.Println(string(b))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -236,7 +253,7 @@ func updateMirrorSize(c *cli.Context) error {
|
||||
)
|
||||
}
|
||||
|
||||
logger.Infof("Successfully updated mirror size to %s", mirrorSize)
|
||||
fmt.Printf("Successfully updated mirror size to %s\n", mirrorSize)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -279,9 +296,9 @@ func removeWorker(c *cli.Context) error {
|
||||
res := map[string]string{}
|
||||
err = json.NewDecoder(resp.Body).Decode(&res)
|
||||
if res["message"] == "deleted" {
|
||||
logger.Info("Successfully removed the worker")
|
||||
fmt.Println("Successfully removed the worker")
|
||||
} else {
|
||||
logger.Info("Failed to remove the worker")
|
||||
return cli.NewExitError("Failed to remove the worker", 1)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -314,7 +331,7 @@ func flushDisabledJobs(c *cli.Context) error {
|
||||
1)
|
||||
}
|
||||
|
||||
logger.Info("Successfully flushed disabled jobs")
|
||||
fmt.Println("Successfully flushed disabled jobs")
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -367,7 +384,7 @@ func cmdJob(cmd tunasync.CmdVerb) cli.ActionFunc {
|
||||
" command: HTTP status code is not 200: %s", body),
|
||||
1)
|
||||
}
|
||||
logger.Info("Succesfully send command")
|
||||
fmt.Println("Successfully send the command")
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -405,7 +422,7 @@ func cmdWorker(cmd tunasync.CmdVerb) cli.ActionFunc {
|
||||
" command: HTTP status code is not 200: %s", body),
|
||||
1)
|
||||
}
|
||||
logger.Info("Succesfully send command")
|
||||
fmt.Println("Successfully send the command")
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -462,6 +479,10 @@ func main() {
|
||||
Name: "verbose, v",
|
||||
Usage: "Enable verbosely logging",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "debug",
|
||||
Usage: "Enable debugging logging",
|
||||
},
|
||||
}
|
||||
cmdFlags := []cli.Flag{
|
||||
cli.StringFlag{
|
||||
|
||||
1
go.mod
1
go.mod
@@ -9,6 +9,7 @@ require (
|
||||
github.com/codeskyblue/go-sh v0.0.0-20190412065543-76bd3d59ff27
|
||||
github.com/dennwc/btrfs v0.0.0-20190517175702-d917b30ff035
|
||||
github.com/gin-gonic/gin v1.5.0
|
||||
github.com/imdario/mergo v0.3.9
|
||||
github.com/mattn/goveralls v0.0.5 // indirect
|
||||
github.com/pkg/profile v1.4.0
|
||||
github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46
|
||||
|
||||
5
go.sum
5
go.sum
@@ -29,6 +29,9 @@ github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/imdario/mergo v0.3.9 h1:UauaLniWCFHWd+Jp9oCEkTBj8VO/9DKg3PV3VCNMDIg=
|
||||
github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo=
|
||||
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
@@ -38,7 +41,9 @@ github.com/mattn/go-isatty v0.0.9 h1:d5US/mDsogSGW37IV293h//ZFaeajb69h+EHFsv2xGg
|
||||
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
||||
github.com/mattn/goveralls v0.0.5 h1:spfq8AyZ0cCk57Za6/juJ5btQxeE1FaEGMdfcI+XO48=
|
||||
github.com/mattn/goveralls v0.0.5/go.mod h1:Xg2LHi51faXLyKXwsndxiW6uxEEQT9+3sjGzzwU4xy0=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/pkg/profile v1.4.0 h1:uCmaf4vVbWAOZz36k1hrQD7ijGRzLwaME8Am/7a4jZI=
|
||||
github.com/pkg/profile v1.4.0/go.mod h1:NWz/XGvpEW1FyYQ7fCx4dqYBLlfTcE+A9FLAkNKqjFE=
|
||||
|
||||
@@ -24,9 +24,12 @@ func InitLogger(verbose, debug, withSystemd bool) {
|
||||
|
||||
if debug {
|
||||
logging.SetLevel(logging.DEBUG, "tunasync")
|
||||
logging.SetLevel(logging.DEBUG, "tunasynctl")
|
||||
} else if verbose {
|
||||
logging.SetLevel(logging.INFO, "tunasync")
|
||||
logging.SetLevel(logging.INFO, "tunasynctl")
|
||||
} else {
|
||||
logging.SetLevel(logging.NOTICE, "tunasync")
|
||||
logging.SetLevel(logging.NOTICE, "tunasynctl")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,12 +6,37 @@ import (
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"time"
|
||||
)
|
||||
|
||||
var rsyncExitValues = map[int]string{
|
||||
0: "Success",
|
||||
1: "Syntax or usage error",
|
||||
2: "Protocol incompatibility",
|
||||
3: "Errors selecting input/output files, dirs",
|
||||
4: "Requested action not supported: an attempt was made to manipulate 64-bit files on a platform that cannot support them; or an option was specified that is supported by the client and not by the server.",
|
||||
5: "Error starting client-server protocol",
|
||||
6: "Daemon unable to append to log-file",
|
||||
10: "Error in socket I/O",
|
||||
11: "Error in file I/O",
|
||||
12: "Error in rsync protocol data stream",
|
||||
13: "Errors with program diagnostics",
|
||||
14: "Error in IPC code",
|
||||
20: "Received SIGUSR1 or SIGINT",
|
||||
21: "Some error returned by waitpid()",
|
||||
22: "Error allocating core memory buffers",
|
||||
23: "Partial transfer due to error",
|
||||
24: "Partial transfer due to vanished source files",
|
||||
25: "The --max-delete limit stopped deletions",
|
||||
30: "Timeout in data send/receive",
|
||||
35: "Timeout waiting for daemon connection",
|
||||
}
|
||||
|
||||
// GetTLSConfig generate tls.Config from CAFile
|
||||
func GetTLSConfig(CAFile string) (*tls.Config, error) {
|
||||
caCert, err := ioutil.ReadFile(CAFile)
|
||||
@@ -115,3 +140,16 @@ func ExtractSizeFromRsyncLog(logFile string) string {
|
||||
re := regexp.MustCompile(`(?m)^Total file size: ([0-9\.]+[KMGTP]?) bytes`)
|
||||
return ExtractSizeFromLog(logFile, re)
|
||||
}
|
||||
|
||||
// TranslateRsyncErrorCode translates the exit code of rsync to a message
|
||||
func TranslateRsyncErrorCode(cmdErr error) (exitCode int, msg string) {
|
||||
|
||||
if exiterr, ok := cmdErr.(*exec.ExitError); ok {
|
||||
exitCode = exiterr.ExitCode()
|
||||
strerr, valid := rsyncExitValues[exitCode]
|
||||
if valid {
|
||||
msg = fmt.Sprintf("rsync error: %s", strerr)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package internal
|
||||
|
||||
// Version of the program
|
||||
const Version string = "0.5.1"
|
||||
const Version string = "0.6.0"
|
||||
|
||||
@@ -19,6 +19,7 @@ type baseProvider struct {
|
||||
isMaster bool
|
||||
|
||||
cmd *cmdJob
|
||||
logFileFd *os.File
|
||||
isRunning atomic.Value
|
||||
|
||||
cgroup *cgroupHook
|
||||
@@ -128,10 +129,19 @@ func (p *baseProvider) prepareLogFile(append bool) error {
|
||||
logger.Errorf("Error opening logfile %s: %s", p.LogFile(), err.Error())
|
||||
return err
|
||||
}
|
||||
p.logFileFd = logFile
|
||||
p.cmd.SetLogFile(logFile)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *baseProvider) closeLogFile() (err error) {
|
||||
if p.logFileFd != nil {
|
||||
err = p.logFileFd.Close()
|
||||
p.logFileFd = nil
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (p *baseProvider) Run() error {
|
||||
panic("Not Implemented")
|
||||
}
|
||||
|
||||
@@ -88,6 +88,7 @@ func (p *cmdProvider) DataSize() string {
|
||||
|
||||
func (p *cmdProvider) Run() error {
|
||||
p.dataSize = ""
|
||||
defer p.closeLogFile()
|
||||
if err := p.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -96,7 +97,7 @@ func (p *cmdProvider) Run() error {
|
||||
}
|
||||
if p.failOnMatch != nil {
|
||||
matches, err := internal.FindAllSubmatchInFile(p.LogFile(), p.failOnMatch)
|
||||
fmt.Printf("FindAllSubmatchInFile: %q\n", matches)
|
||||
logger.Infof("FindAllSubmatchInFile: %q\n", matches)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"path/filepath"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
"github.com/imdario/mergo"
|
||||
)
|
||||
|
||||
type providerEnum uint8
|
||||
@@ -41,7 +42,8 @@ type Config struct {
|
||||
BtrfsSnapshot btrfsSnapshotConfig `toml:"btrfs_snapshot"`
|
||||
Docker dockerConfig `toml:"docker"`
|
||||
Include includeConfig `toml:"include"`
|
||||
Mirrors []mirrorConfig `toml:"mirrors"`
|
||||
MirrorsConf []mirrorConfig `toml:"mirrors"`
|
||||
Mirrors []mirrorConfig
|
||||
}
|
||||
|
||||
type globalConfig struct {
|
||||
@@ -111,15 +113,16 @@ type includedMirrorConfig struct {
|
||||
}
|
||||
|
||||
type mirrorConfig struct {
|
||||
Name string `toml:"name"`
|
||||
Provider providerEnum `toml:"provider"`
|
||||
Upstream string `toml:"upstream"`
|
||||
Interval int `toml:"interval"`
|
||||
Retry int `toml:"retry"`
|
||||
MirrorDir string `toml:"mirror_dir"`
|
||||
LogDir string `toml:"log_dir"`
|
||||
Env map[string]string `toml:"env"`
|
||||
Role string `toml:"role"`
|
||||
Name string `toml:"name"`
|
||||
Provider providerEnum `toml:"provider"`
|
||||
Upstream string `toml:"upstream"`
|
||||
Interval int `toml:"interval"`
|
||||
Retry int `toml:"retry"`
|
||||
MirrorDir string `toml:"mirror_dir"`
|
||||
MirrorSubDir string `toml:"mirror_subdir"`
|
||||
LogDir string `toml:"log_dir"`
|
||||
Env map[string]string `toml:"env"`
|
||||
Role string `toml:"role"`
|
||||
|
||||
// These two options over-write the global options
|
||||
ExecOnSuccess []string `toml:"exec_on_success"`
|
||||
@@ -148,6 +151,8 @@ type mirrorConfig struct {
|
||||
DockerOptions []string `toml:"docker_options"`
|
||||
|
||||
SnapshotPath string `toml:"snapshot_path"`
|
||||
|
||||
ChildMirrors []mirrorConfig `toml:"mirrors"`
|
||||
}
|
||||
|
||||
// LoadConfig loads configuration
|
||||
@@ -174,9 +179,36 @@ func LoadConfig(cfgFile string) (*Config, error) {
|
||||
logger.Errorf(err.Error())
|
||||
return nil, err
|
||||
}
|
||||
cfg.Mirrors = append(cfg.Mirrors, incMirCfg.Mirrors...)
|
||||
cfg.MirrorsConf = append(cfg.MirrorsConf, incMirCfg.Mirrors...)
|
||||
}
|
||||
}
|
||||
|
||||
for _, m := range cfg.MirrorsConf {
|
||||
if err := recursiveMirrors(cfg, nil, m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
func recursiveMirrors(cfg *Config, parent *mirrorConfig, mirror mirrorConfig) error {
|
||||
var curMir mirrorConfig
|
||||
if parent != nil {
|
||||
curMir = *parent
|
||||
}
|
||||
curMir.ChildMirrors = nil
|
||||
if err := mergo.Merge(&curMir, mirror, mergo.WithOverride); err != nil {
|
||||
return err
|
||||
}
|
||||
if mirror.ChildMirrors == nil {
|
||||
cfg.Mirrors = append(cfg.Mirrors, curMir)
|
||||
} else {
|
||||
for _, m := range mirror.ChildMirrors {
|
||||
if err := recursiveMirrors(cfg, &curMir, m); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -83,9 +83,9 @@ exec_on_failure = [
|
||||
tmpDir,
|
||||
)
|
||||
|
||||
cfgBlob = cfgBlob + incSection
|
||||
curCfgBlob := cfgBlob + incSection
|
||||
|
||||
err = ioutil.WriteFile(tmpfile.Name(), []byte(cfgBlob), 0644)
|
||||
err = ioutil.WriteFile(tmpfile.Name(), []byte(curCfgBlob), 0644)
|
||||
So(err, ShouldEqual, nil)
|
||||
defer tmpfile.Close()
|
||||
|
||||
@@ -157,6 +157,102 @@ use_ipv6 = true
|
||||
So(len(cfg.Mirrors), ShouldEqual, 6)
|
||||
})
|
||||
|
||||
Convey("Everything should work on nested config file", t, func() {
|
||||
tmpfile, err := ioutil.TempFile("", "tunasync")
|
||||
So(err, ShouldEqual, nil)
|
||||
defer os.Remove(tmpfile.Name())
|
||||
|
||||
tmpDir, err := ioutil.TempDir("", "tunasync")
|
||||
So(err, ShouldBeNil)
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
incSection := fmt.Sprintf(
|
||||
"\n[include]\n"+
|
||||
"include_mirrors = \"%s/*.conf\"",
|
||||
tmpDir,
|
||||
)
|
||||
|
||||
curCfgBlob := cfgBlob + incSection
|
||||
|
||||
err = ioutil.WriteFile(tmpfile.Name(), []byte(curCfgBlob), 0644)
|
||||
So(err, ShouldEqual, nil)
|
||||
defer tmpfile.Close()
|
||||
|
||||
incBlob1 := `
|
||||
[[mirrors]]
|
||||
name = "ipv6s"
|
||||
use_ipv6 = true
|
||||
[[mirrors.mirrors]]
|
||||
name = "debians"
|
||||
mirror_subdir = "debian"
|
||||
provider = "two-stage-rsync"
|
||||
stage1_profile = "debian"
|
||||
|
||||
[[mirrors.mirrors.mirrors]]
|
||||
name = "debian-security"
|
||||
upstream = "rsync://test.host/debian-security/"
|
||||
[[mirrors.mirrors.mirrors]]
|
||||
name = "ubuntu"
|
||||
stage1_profile = "ubuntu"
|
||||
upstream = "rsync://test.host2/ubuntu/"
|
||||
[[mirrors.mirrors]]
|
||||
name = "debian-cd"
|
||||
provider = "rsync"
|
||||
upstream = "rsync://test.host3/debian-cd/"
|
||||
`
|
||||
err = ioutil.WriteFile(filepath.Join(tmpDir, "nest.conf"), []byte(incBlob1), 0644)
|
||||
So(err, ShouldEqual, nil)
|
||||
|
||||
cfg, err := LoadConfig(tmpfile.Name())
|
||||
So(err, ShouldBeNil)
|
||||
So(cfg.Global.Name, ShouldEqual, "test_worker")
|
||||
So(cfg.Global.Interval, ShouldEqual, 240)
|
||||
So(cfg.Global.Retry, ShouldEqual, 3)
|
||||
So(cfg.Global.MirrorDir, ShouldEqual, "/data/mirrors")
|
||||
|
||||
So(cfg.Manager.APIBase, ShouldEqual, "https://127.0.0.1:5000")
|
||||
So(cfg.Server.Hostname, ShouldEqual, "worker1.example.com")
|
||||
|
||||
m := cfg.Mirrors[0]
|
||||
So(m.Name, ShouldEqual, "AOSP")
|
||||
So(m.MirrorDir, ShouldEqual, "/data/git/AOSP")
|
||||
So(m.Provider, ShouldEqual, provCommand)
|
||||
So(m.Interval, ShouldEqual, 720)
|
||||
So(m.Retry, ShouldEqual, 2)
|
||||
So(m.Env["REPO"], ShouldEqual, "/usr/local/bin/aosp-repo")
|
||||
|
||||
m = cfg.Mirrors[1]
|
||||
So(m.Name, ShouldEqual, "debian")
|
||||
So(m.MirrorDir, ShouldEqual, "")
|
||||
So(m.Provider, ShouldEqual, provTwoStageRsync)
|
||||
|
||||
m = cfg.Mirrors[2]
|
||||
So(m.Name, ShouldEqual, "fedora")
|
||||
So(m.MirrorDir, ShouldEqual, "")
|
||||
So(m.Provider, ShouldEqual, provRsync)
|
||||
So(m.ExcludeFile, ShouldEqual, "/etc/tunasync.d/fedora-exclude.txt")
|
||||
|
||||
m = cfg.Mirrors[3]
|
||||
So(m.Name, ShouldEqual, "debian-security")
|
||||
So(m.MirrorDir, ShouldEqual, "")
|
||||
So(m.Provider, ShouldEqual, provTwoStageRsync)
|
||||
So(m.UseIPv6, ShouldEqual, true)
|
||||
So(m.Stage1Profile, ShouldEqual, "debian")
|
||||
|
||||
m = cfg.Mirrors[4]
|
||||
So(m.Name, ShouldEqual, "ubuntu")
|
||||
So(m.MirrorDir, ShouldEqual, "")
|
||||
So(m.Provider, ShouldEqual, provTwoStageRsync)
|
||||
So(m.UseIPv6, ShouldEqual, true)
|
||||
So(m.Stage1Profile, ShouldEqual, "ubuntu")
|
||||
|
||||
m = cfg.Mirrors[5]
|
||||
So(m.Name, ShouldEqual, "debian-cd")
|
||||
So(m.UseIPv6, ShouldEqual, true)
|
||||
So(m.Provider, ShouldEqual, provRsync)
|
||||
|
||||
So(len(cfg.Mirrors), ShouldEqual, 6)
|
||||
})
|
||||
Convey("Providers can be inited from a valid config file", t, func() {
|
||||
tmpfile, err := ioutil.TempFile("", "tunasync")
|
||||
So(err, ShouldEqual, nil)
|
||||
@@ -207,4 +303,90 @@ use_ipv6 = true
|
||||
So(rp.excludeFile, ShouldEqual, "/etc/tunasync.d/fedora-exclude.txt")
|
||||
|
||||
})
|
||||
|
||||
Convey("MirrorSubdir should work", t, func() {
|
||||
tmpfile, err := ioutil.TempFile("", "tunasync")
|
||||
So(err, ShouldEqual, nil)
|
||||
defer os.Remove(tmpfile.Name())
|
||||
|
||||
cfgBlob1 := `
|
||||
[global]
|
||||
name = "test_worker"
|
||||
log_dir = "/var/log/tunasync/{{.Name}}"
|
||||
mirror_dir = "/data/mirrors"
|
||||
concurrent = 10
|
||||
interval = 240
|
||||
retry = 3
|
||||
|
||||
[manager]
|
||||
api_base = "https://127.0.0.1:5000"
|
||||
token = "some_token"
|
||||
|
||||
[server]
|
||||
hostname = "worker1.example.com"
|
||||
listen_addr = "127.0.0.1"
|
||||
listen_port = 6000
|
||||
ssl_cert = "/etc/tunasync.d/worker1.cert"
|
||||
ssl_key = "/etc/tunasync.d/worker1.key"
|
||||
|
||||
[[mirrors]]
|
||||
name = "ipv6s"
|
||||
use_ipv6 = true
|
||||
[[mirrors.mirrors]]
|
||||
name = "debians"
|
||||
mirror_subdir = "debian"
|
||||
provider = "two-stage-rsync"
|
||||
stage1_profile = "debian"
|
||||
|
||||
[[mirrors.mirrors.mirrors]]
|
||||
name = "debian-security"
|
||||
upstream = "rsync://test.host/debian-security/"
|
||||
[[mirrors.mirrors.mirrors]]
|
||||
name = "ubuntu"
|
||||
stage1_profile = "ubuntu"
|
||||
upstream = "rsync://test.host2/ubuntu/"
|
||||
[[mirrors.mirrors]]
|
||||
name = "debian-cd"
|
||||
provider = "rsync"
|
||||
upstream = "rsync://test.host3/debian-cd/"
|
||||
`
|
||||
err = ioutil.WriteFile(tmpfile.Name(), []byte(cfgBlob1), 0644)
|
||||
So(err, ShouldEqual, nil)
|
||||
defer tmpfile.Close()
|
||||
|
||||
cfg, err := LoadConfig(tmpfile.Name())
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
providers := map[string]mirrorProvider{}
|
||||
for _, m := range cfg.Mirrors {
|
||||
p := newMirrorProvider(m, cfg)
|
||||
providers[p.Name()] = p
|
||||
}
|
||||
|
||||
p := providers["debian-security"]
|
||||
So(p.Name(), ShouldEqual, "debian-security")
|
||||
So(p.LogDir(), ShouldEqual, "/var/log/tunasync/debian-security")
|
||||
So(p.LogFile(), ShouldEqual, "/var/log/tunasync/debian-security/latest.log")
|
||||
r2p, ok := p.(*twoStageRsyncProvider)
|
||||
So(ok, ShouldBeTrue)
|
||||
So(r2p.stage1Profile, ShouldEqual, "debian")
|
||||
So(r2p.WorkingDir(), ShouldEqual, "/data/mirrors/debian/debian-security")
|
||||
|
||||
p = providers["ubuntu"]
|
||||
So(p.Name(), ShouldEqual, "ubuntu")
|
||||
So(p.LogDir(), ShouldEqual, "/var/log/tunasync/ubuntu")
|
||||
So(p.LogFile(), ShouldEqual, "/var/log/tunasync/ubuntu/latest.log")
|
||||
r2p, ok = p.(*twoStageRsyncProvider)
|
||||
So(ok, ShouldBeTrue)
|
||||
So(r2p.stage1Profile, ShouldEqual, "ubuntu")
|
||||
So(r2p.WorkingDir(), ShouldEqual, "/data/mirrors/debian/ubuntu")
|
||||
|
||||
p = providers["debian-cd"]
|
||||
So(p.Name(), ShouldEqual, "debian-cd")
|
||||
So(p.LogDir(), ShouldEqual, "/var/log/tunasync/debian-cd")
|
||||
So(p.LogFile(), ShouldEqual, "/var/log/tunasync/debian-cd/latest.log")
|
||||
rp, ok := p.(*rsyncProvider)
|
||||
So(ok, ShouldBeTrue)
|
||||
So(rp.WorkingDir(), ShouldEqual, "/data/mirrors/debian-cd")
|
||||
})
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ func newMirrorProvider(mirror mirrorConfig, cfg *Config) mirrorProvider {
|
||||
}
|
||||
if mirrorDir == "" {
|
||||
mirrorDir = filepath.Join(
|
||||
cfg.Global.MirrorDir, mirror.Name,
|
||||
cfg.Global.MirrorDir, mirror.MirrorSubDir, mirror.Name,
|
||||
)
|
||||
}
|
||||
if mirror.Interval == 0 {
|
||||
|
||||
@@ -106,6 +106,34 @@ exit 0
|
||||
})
|
||||
|
||||
})
|
||||
Convey("If the rsync program fails", t, func() {
|
||||
tmpDir, err := ioutil.TempDir("", "tunasync")
|
||||
defer os.RemoveAll(tmpDir)
|
||||
So(err, ShouldBeNil)
|
||||
tmpFile := filepath.Join(tmpDir, "log_file")
|
||||
|
||||
Convey("in the rsyncProvider", func() {
|
||||
|
||||
c := rsyncConfig{
|
||||
name: "tuna",
|
||||
upstreamURL: "rsync://rsync.tuna.moe/tuna/",
|
||||
workingDir: tmpDir,
|
||||
logDir: tmpDir,
|
||||
logFile: tmpFile,
|
||||
extraOptions: []string{"--somethine-invalid"},
|
||||
interval: 600 * time.Second,
|
||||
}
|
||||
|
||||
provider, err := newRsyncProvider(c)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
err = provider.Run()
|
||||
So(err, ShouldNotBeNil)
|
||||
loggedContent, err := ioutil.ReadFile(provider.LogFile())
|
||||
So(err, ShouldBeNil)
|
||||
So(string(loggedContent), ShouldContainSubstring, "Syntax or usage error")
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestRsyncProviderWithAuthentication(t *testing.T) {
|
||||
@@ -316,7 +344,7 @@ echo $AOSP_REPO_BIN
|
||||
|
||||
Convey("If a long job is killed", func(ctx C) {
|
||||
scriptContent := `#!/bin/bash
|
||||
sleep 5
|
||||
sleep 10
|
||||
`
|
||||
err = ioutil.WriteFile(scriptFile, []byte(scriptContent), 0755)
|
||||
So(err, ShouldBeNil)
|
||||
@@ -528,7 +556,7 @@ exit 0
|
||||
Convey("Try terminating", func(ctx C) {
|
||||
scriptContent := `#!/bin/bash
|
||||
echo $@
|
||||
sleep 4
|
||||
sleep 10
|
||||
exit 0
|
||||
`
|
||||
err = ioutil.WriteFile(scriptFile, []byte(scriptContent), 0755)
|
||||
@@ -552,8 +580,38 @@ exit 0
|
||||
|
||||
loggedContent, err := ioutil.ReadFile(provider.LogFile())
|
||||
So(err, ShouldBeNil)
|
||||
So(string(loggedContent), ShouldEqual, expectedOutput)
|
||||
So(string(loggedContent), ShouldStartWith, expectedOutput)
|
||||
// fmt.Println(string(loggedContent))
|
||||
})
|
||||
})
|
||||
|
||||
Convey("If the rsync program fails", t, func(ctx C) {
|
||||
tmpDir, err := ioutil.TempDir("", "tunasync")
|
||||
defer os.RemoveAll(tmpDir)
|
||||
So(err, ShouldBeNil)
|
||||
tmpFile := filepath.Join(tmpDir, "log_file")
|
||||
|
||||
Convey("in the twoStageRsyncProvider", func() {
|
||||
|
||||
c := twoStageRsyncConfig{
|
||||
name: "tuna-two-stage-rsync",
|
||||
upstreamURL: "rsync://0.0.0.1/",
|
||||
stage1Profile: "debian",
|
||||
workingDir: tmpDir,
|
||||
logDir: tmpDir,
|
||||
logFile: tmpFile,
|
||||
excludeFile: tmpFile,
|
||||
}
|
||||
|
||||
provider, err := newTwoStageRsyncProvider(c)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
err = provider.Run()
|
||||
So(err, ShouldNotBeNil)
|
||||
loggedContent, err := ioutil.ReadFile(provider.LogFile())
|
||||
So(err, ShouldBeNil)
|
||||
So(string(loggedContent), ShouldContainSubstring, "Error in socket I/O")
|
||||
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -105,10 +105,18 @@ func (p *rsyncProvider) DataSize() string {
|
||||
|
||||
func (p *rsyncProvider) Run() error {
|
||||
p.dataSize = ""
|
||||
defer p.closeLogFile()
|
||||
if err := p.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := p.Wait(); err != nil {
|
||||
code, msg := internal.TranslateRsyncErrorCode(err)
|
||||
if code != 0 {
|
||||
logger.Debug("Rsync exitcode %d (%s)", code, msg)
|
||||
if p.logFileFd != nil {
|
||||
p.logFileFd.WriteString(msg + "\n")
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
p.dataSize = internal.ExtractSizeFromRsyncLog(p.LogFile())
|
||||
|
||||
@@ -118,9 +118,6 @@ func (c *cmdJob) Wait() error {
|
||||
return c.retErr
|
||||
default:
|
||||
err := c.cmd.Wait()
|
||||
if c.cmd.Stdout != nil {
|
||||
c.cmd.Stdout.(*os.File).Close()
|
||||
}
|
||||
c.retErr = err
|
||||
close(c.finished)
|
||||
return err
|
||||
|
||||
@@ -156,6 +156,7 @@ func (p *twoStageRsyncProvider) Run() error {
|
||||
if err := p.prepareLogFile(stage > 1); err != nil {
|
||||
return err
|
||||
}
|
||||
defer p.closeLogFile()
|
||||
|
||||
if err = p.cmd.Start(); err != nil {
|
||||
return err
|
||||
@@ -167,6 +168,13 @@ func (p *twoStageRsyncProvider) Run() error {
|
||||
err = p.Wait()
|
||||
p.Lock()
|
||||
if err != nil {
|
||||
code, msg := internal.TranslateRsyncErrorCode(err)
|
||||
if code != 0 {
|
||||
logger.Debug("Rsync exitcode %d (%s)", code, msg)
|
||||
if p.logFileFd != nil {
|
||||
p.logFileFd.WriteString(msg + "\n")
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
在新工单中引用
屏蔽一个用户