diff --git a/.gitignore b/.gitignore index 528694a..796b96d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1 @@ -*.swp -*~ -/*.cov -node_modules +/build diff --git a/.testandcover.bash b/.testandcover.bash index 8f88b18..572daea 100755 --- a/.testandcover.bash +++ b/.testandcover.bash @@ -8,6 +8,8 @@ function die() { export GOPATH=`pwd`:$GOPATH +make + # Initialize profile.cov echo "mode: count" > profile.cov diff --git a/.travis.yml b/.travis.yml index c943565..a02a8cb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,7 @@ go: before_install: - sudo apt-get install cgroup-bin + - go get github.com/smartystreets/goconvey - go get golang.org/x/tools/cmd/cover - go get -v github.com/mattn/goveralls diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..b4f9aae --- /dev/null +++ b/Makefile @@ -0,0 +1,16 @@ +LDFLAGS="-X main.buildstamp=`date -u '+%s'` -X main.githash=`git rev-parse HEAD`" + +all: get tunasync tunasynctl + +get: + go get ./cmd/tunasync + go get ./cmd/tunasynctl + +build: + mkdir -p build + +tunasync: build + go build -o build/tunasync -ldflags ${LDFLAGS} github.com/tuna/tunasync/cmd/tunasync + +tunasynctl: build + go build -o build/tunasynctl -ldflags ${LDFLAGS} github.com/tuna/tunasync/cmd/tunasynctl diff --git a/cmd/tunasync/tunasync.go b/cmd/tunasync/tunasync.go index 3cd0c20..0a29552 100644 --- a/cmd/tunasync/tunasync.go +++ b/cmd/tunasync/tunasync.go @@ -1,8 +1,10 @@ package main import ( + "fmt" "os" "os/signal" + "strconv" "syscall" "time" @@ -16,6 +18,11 @@ import ( "github.com/tuna/tunasync/worker" ) +var ( + buildstamp = "" + githash = "No githash provided" +) + var logger = logging.MustGetLogger("tunasync") func startManager(c *cli.Context) { @@ -99,6 +106,28 @@ func startWorker(c *cli.Context) { } func main() { + + cli.VersionPrinter = func(c *cli.Context) { + var builddate string + if buildstamp == "" { + builddate = "No build date provided" + } else { + ts, err := strconv.Atoi(buildstamp) + if err != nil { + builddate = "No build date provided" + } else { + t := time.Unix(int64(ts), 0) + builddate = t.String() + } + } + fmt.Printf( + "Version: %s\n"+ + "Git Hash: %s\n"+ + "Build Date: %s\n", + c.App.Version, githash, builddate, + ) + } + app := cli.NewApp() app.EnableBashCompletion = true app.Version = "0.1" diff --git a/cmd/tunasynctl/tunasynctl.go b/cmd/tunasynctl/tunasynctl.go index 98e51e2..71f4751 100644 --- a/cmd/tunasynctl/tunasynctl.go +++ b/cmd/tunasynctl/tunasynctl.go @@ -6,7 +6,9 @@ import ( "io/ioutil" "net/http" "os" + "strconv" "strings" + "time" "github.com/BurntSushi/toml" "github.com/codegangsta/cli" @@ -15,6 +17,11 @@ import ( tunasync "github.com/tuna/tunasync/internal" ) +var ( + buildstamp = "" + githash = "No githash provided" +) + const ( listJobsPath = "/jobs" listWorkersPath = "/workers" @@ -225,7 +232,61 @@ func cmdJob(cmd tunasync.CmdVerb) cli.ActionFunc { } } +func cmdWorker(cmd tunasync.CmdVerb) cli.ActionFunc { + return func(c *cli.Context) error { + cmd := tunasync.ClientCmd{ + Cmd: cmd, + WorkerID: c.String("worker"), + } + resp, err := tunasync.PostJSON(baseURL+cmdPath, cmd, client) + if err != nil { + return cli.NewExitError( + fmt.Sprintf("Failed to correctly send command: %s", + err.Error()), + 1) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return cli.NewExitError( + fmt.Sprintf("Failed to parse response: %s", err.Error()), + 1) + } + + return cli.NewExitError(fmt.Sprintf("Failed to correctly send"+ + " command: HTTP status code is not 200: %s", body), + 1) + } + logger.Info("Succesfully send command") + + return nil + } +} + func main() { + cli.VersionPrinter = func(c *cli.Context) { + var builddate string + if buildstamp == "" { + builddate = "No build date provided" + } else { + ts, err := strconv.Atoi(buildstamp) + if err != nil { + builddate = "No build date provided" + } else { + t := time.Unix(int64(ts), 0) + builddate = t.String() + } + } + fmt.Printf( + "Version: %s\n"+ + "Git Hash: %s\n"+ + "Build Date: %s\n", + c.App.Version, githash, builddate, + ) + } + app := cli.NewApp() app.EnableBashCompletion = true app.Version = "0.1" @@ -304,6 +365,12 @@ func main() { Flags: append(commonFlags, cmdFlags...), Action: initializeWrapper(cmdJob(tunasync.CmdRestart)), }, + { + Name: "reload", + Usage: "Tell worker to reload configurations", + Flags: append(commonFlags, cmdFlags...), + Action: initializeWrapper(cmdWorker(tunasync.CmdReload)), + }, { Name: "ping", Flags: append(commonFlags, cmdFlags...), diff --git a/internal/msg.go b/internal/msg.go index 85337d6..b1164ea 100644 --- a/internal/msg.go +++ b/internal/msg.go @@ -35,6 +35,7 @@ const ( CmdDisable // disable the job (stops goroutine) CmdRestart // restart syncing CmdPing // ensure the goroutine is alive + CmdReload // reload mirror config ) func (c CmdVerb) String() string { @@ -49,6 +50,8 @@ func (c CmdVerb) String() string { return "restart" case CmdPing: return "ping" + case CmdReload: + return "reload" } return "unknown" } diff --git a/worker/worker.go b/worker/worker.go index eff7205..4b6741b 100644 --- a/worker/worker.go +++ b/worker/worker.go @@ -3,7 +3,9 @@ package worker import ( "fmt" "net/http" + "os" "sync" + "syscall" "time" "github.com/gin-gonic/gin" @@ -185,16 +187,32 @@ func (w *Worker) makeHTTPServer() { return } + logger.Noticef("Received command: %v", cmd) + + if cmd.MirrorID == "" { + // worker-level commands + switch cmd.Cmd { + case CmdReload: + // send myself a SIGHUP + pid := os.Getpid() + syscall.Kill(pid, syscall.SIGHUP) + default: + c.JSON(http.StatusNotAcceptable, gin.H{"msg": "Invalid Command"}) + return + } + } + + // job level comands job, ok := w.jobs[cmd.MirrorID] if !ok { c.JSON(http.StatusNotFound, gin.H{"msg": fmt.Sprintf("Mirror ``%s'' not found", cmd.MirrorID)}) return } - logger.Noticef("Received command: %v", cmd) // No matter what command, the existing job // schedule should be flushed w.schedule.Remove(job.Name()) + // if job disabled, start them first switch cmd.Cmd { case CmdStart, CmdRestart: