Commit 5e234e16 authored by Kamil Trzciński's avatar Kamil Trzciński 🔴

Use kardianos/service module

parent 722b9ba5
......@@ -7,6 +7,7 @@ env:
install:
- go get github.com/tools/godep
- go get -u github.com/golang/lint/golint
- go get code.google.com/p/winsvc/eventlog
- godep restore
before_script:
- make version
......
......@@ -4,6 +4,7 @@ v 0.1.13
- Select default shell for OS (bash for Unix, batch for Windows)
- Added Windows Cmd support
- Added Windows PowerShell support
- Added the kardianos/service which allows to easily run gitlab-ci-multi-runner as service on different platforms
v 0.1.12
- Abort all jobs if interrupt or SIGTERM is received
......
......@@ -60,6 +60,14 @@
{
"ImportPath": "github.com/fsouza/go-dockerclient",
"Rev": "5da2a37ecfb5bcb921a36dbc18e912c3235f139a"
},
{
"ImportPath": "github.com/kardianos/osext",
"Rev": "efacde03154693404c65e7aa7d461ac9014acd0c"
},
{
"ImportPath": "github.com/kardianos/service",
"Rev": "9a30c55e3f15ea76eab5c4c42d9b9b5614437ddb"
}
]
}
......@@ -8,9 +8,12 @@ import (
"time"
"github.com/codegangsta/cli"
"github.com/kardianos/service"
log "github.com/Sirupsen/logrus"
"errors"
"fmt"
"github.com/ayufan/gitlab-ci-multi-runner/common"
)
......@@ -20,15 +23,18 @@ type RunnerHealth struct {
}
type MultiRunner struct {
config *common.Config
configFile string
allBuilds []*common.Build
builds []*common.Build
buildsLock sync.RWMutex
healthy map[string]*RunnerHealth
healthyLock sync.Mutex
finished bool
abortBuilds chan os.Signal
config *common.Config
configFile string
listenAddr string
allBuilds []*common.Build
builds []*common.Build
buildsLock sync.RWMutex
healthy map[string]*RunnerHealth
healthyLock sync.Mutex
finished bool
abortBuilds chan os.Signal
interruptSignal chan os.Signal
reloadSignal chan os.Signal
}
func (mr *MultiRunner) errorln(args ...interface{}) {
......@@ -217,13 +223,12 @@ func (mr *MultiRunner) loadConfig() error {
return nil
}
func RunMulti(c *cli.Context) {
mr := MultiRunner{
configFile: c.String("config"),
allBuilds: []*common.Build{},
builds: []*common.Build{},
abortBuilds: make(chan os.Signal),
}
func (mr *MultiRunner) Start(s service.Service) error {
mr.allBuilds = []*common.Build{}
mr.builds = []*common.Build{}
mr.abortBuilds = make(chan os.Signal)
mr.interruptSignal = make(chan os.Signal)
mr.reloadSignal = make(chan os.Signal, 1)
mr.println("Starting multi-runner from", mr.configFile, "...")
......@@ -232,16 +237,23 @@ func RunMulti(c *cli.Context) {
panic(err)
}
// Start webserver
if listenAddr := c.String("listen-addr"); len(listenAddr) > 0 {
// Start web server
if len(mr.listenAddr) > 0 {
mrs := MultiRunnerServer{
MultiRunner: &mr,
listenAddresses: []string{listenAddr},
MultiRunner: mr,
listenAddresses: []string{mr.listenAddr},
}
go mrs.Run()
}
// Start should not block. Do the actual work async.
go mr.Run()
return nil
}
func (mr *MultiRunner) Run() {
runners := make(chan *common.RunnerConfig)
go mr.feedRunners(runners)
......@@ -249,11 +261,7 @@ func RunMulti(c *cli.Context) {
stopWorker := make(chan bool)
go mr.startWorkers(startWorker, stopWorker, runners)
interruptSignal := make(chan os.Signal, 2)
signal.Notify(interruptSignal, os.Interrupt, syscall.SIGTERM)
reloadSignal := make(chan os.Signal, 1)
signal.Notify(reloadSignal, syscall.SIGHUP)
signal.Notify(mr.reloadSignal, syscall.SIGHUP)
currentWorkers := 0
workerIndex := 0
......@@ -267,7 +275,7 @@ finish_worker:
for currentWorkers > buildLimit {
select {
case stopWorker <- true:
case signaled = <-interruptSignal:
case signaled = <-mr.interruptSignal:
break finish_worker
}
currentWorkers--
......@@ -276,7 +284,7 @@ finish_worker:
for currentWorkers < buildLimit {
select {
case startWorker <- workerIndex:
case signaled = <-interruptSignal:
case signaled = <-mr.interruptSignal:
break finish_worker
}
currentWorkers++
......@@ -305,7 +313,7 @@ finish_worker:
mr.println("Config reloaded.")
case <-reloadSignal:
case <-mr.reloadSignal:
err := mr.loadConfig()
if err != nil {
mr.errorln("Failed to load config", err)
......@@ -314,16 +322,12 @@ finish_worker:
mr.println("Config reloaded.")
case signaled = <-interruptSignal:
case signaled = <-mr.interruptSignal:
break finish_worker
}
}
mr.errorln("Received signal:", signaled)
mr.finished = true
close := make(chan int)
// Pump signal to abort all builds
go func() {
for {
......@@ -331,39 +335,73 @@ finish_worker:
}
}()
// Watch for second signal which will force to close process
go func() {
newSignal := <-interruptSignal
mr.errorln("Forced exit:", newSignal)
close <- 1
}()
// Wait for workers to shutdown
go func() {
for currentWorkers > 0 {
stopWorker <- true
currentWorkers--
}
mr.println("All workers stopped. Can exit now")
close <- 0
}()
for currentWorkers > 0 {
stopWorker <- true
currentWorkers--
}
mr.println("All workers stopped. Can exit now")
os.Exit(0)
}
// Timeout shutdown
go func() {
time.Sleep(common.ShutdownTimeout * time.Second)
mr.errorln("Shutdown timedout.")
close <- 1
}()
func (mr *MultiRunner) Stop(s service.Service) error {
mr.errorln("Requested service stop")
mr.interruptSignal <- os.Interrupt
os.Exit(<-close)
signals := make(chan os.Signal)
signal.Notify(signals, os.Interrupt, syscall.SIGTERM)
select {
case newSignal := <-signals:
return fmt.Errorf("forced exit:", newSignal)
case <-time.After(common.ShutdownTimeout * time.Second):
return errors.New("shutdown timedout")
}
}
var (
CmdRunMulti = cli.Command{
func CreateService(c *cli.Context) service.Service {
svcConfig := &service.Config{
Name: "gitlab-ci-multi-runner",
DisplayName: "GitLab-CI Multi-purpose Runner",
Description: "Unofficial GitLab CI runner written in Go",
Arguments: []string{"run"},
}
mr := &MultiRunner{
configFile: c.String("config"),
listenAddr: c.String("listen-addr"),
}
s, err := service.New(mr, svcConfig)
if err != nil {
log.Fatal(err)
}
return s
}
func RunService(c *cli.Context) {
s := CreateService(c)
logger, err := s.Logger(nil)
if err != nil {
log.Fatal(err)
}
if !service.Interactive() {
log.AddHook(&ServiceLogHook{logger})
}
err = s.Run()
if err != nil {
logger.Error(err)
}
}
func init() {
common.RegisterCommand(cli.Command{
Name: "run",
ShortName: "r",
Usage: "run multi runner",
Action: RunMulti,
Usage: "run multi runner service",
Action: RunService,
Flags: []cli.Flag{
cli.StringFlag{
Name: "docker-host",
......@@ -384,5 +422,5 @@ var (
EnvVar: "API_LISTEN",
},
},
}
)
})
}
package commands
import (
log "github.com/Sirupsen/logrus"
"github.com/ayufan/gitlab-ci-multi-runner/common"
"github.com/codegangsta/cli"
"github.com/kardianos/service"
"os"
"os/user"
"runtime"
)
type ServiceLogHook struct {
service.Logger
}
func (s *ServiceLogHook) Levels() []log.Level {
return []log.Level{
log.PanicLevel,
log.FatalLevel,
log.ErrorLevel,
log.WarnLevel,
log.InfoLevel,
}
}
func (s *ServiceLogHook) Fire(e *log.Entry) error {
switch e.Level {
case log.PanicLevel, log.FatalLevel, log.ErrorLevel:
s.Error(e.Message)
case log.WarnLevel:
s.Warning(e.Message)
case log.InfoLevel:
s.Info(e.Message)
}
return nil
}
type NullService struct {
}
func (n *NullService) Start(s service.Service) error {
return nil
}
func (n *NullService) Stop(s service.Service) error {
return nil
}
func RunServiceControl(c *cli.Context) {
svcConfig := &service.Config{
Name: c.String("service-name"),
DisplayName: "GitLab-CI Multi-purpose Runner",
Description: "Unofficial GitLab CI runner written in Go",
Arguments: []string{"run"},
UserName: c.String("user"),
}
if runtime.GOOS == "darwin" {
svcConfig.UserService = true
svcConfig.Option = service.KeyValue{
"KeepAlive": true,
"RunAtLoad": true,
}
}
if config := c.String("config"); config != "" {
svcConfig.Arguments = append(svcConfig.Arguments, "--config", config)
}
s, err := service.New(&NullService{}, svcConfig)
if err != nil {
log.Fatal(err)
}
err = service.Control(s, c.Command.Name)
if err != nil {
log.Fatal(err)
}
}
func getCurrentUserName() string {
user, _ := user.Current()
if user != nil {
return user.Username
}
return ""
}
func init() {
flags := []cli.Flag{
cli.StringFlag{
Name: "service-name, n",
Value: "gitlab-ci-multi-runner",
Usage: "Use different names for different services",
},
cli.StringFlag{
Name: "config, c",
Value: "config.toml",
Usage: "Specify custom config file",
},
}
if runtime.GOOS != "darwin" {
flags = append(flags, cli.StringFlag{
Name: "user, u",
Value: getCurrentUserName(),
Usage: "Specify user-name to secure the runner",
})
}
common.RegisterCommand(cli.Command{
Name: "install",
Usage: "install service",
Action: RunServiceControl,
Flags: flags,
})
common.RegisterCommand(cli.Command{
Name: "uninstall",
Usage: "uninstall service",
Action: RunServiceControl,
Flags: flags,
})
common.RegisterCommand(cli.Command{
Name: "start",
Usage: "start service",
Action: RunServiceControl,
Flags: flags,
})
common.RegisterCommand(cli.Command{
Name: "stop",
Usage: "stop service",
Action: RunServiceControl,
Flags: flags,
})
common.RegisterCommand(cli.Command{
Name: "restart",
Usage: "restart service",
Action: RunServiceControl,
Flags: flags,
})
}
......@@ -232,8 +232,8 @@ func getHostname() string {
return hostname
}
var (
CmdRunSetup = cli.Command{
func init() {
common.RegisterCommand(cli.Command{
Name: "setup",
ShortName: "s",
Usage: "setup a new runner",
......@@ -357,5 +357,5 @@ var (
EnvVar: "SSH_USER",
},
},
}
)
})
}
......@@ -92,12 +92,11 @@ func runSingle(c *cli.Context) {
}
}
var (
CmdRunSingle = cli.Command{
Name: "run-single",
ShortName: "rs",
Usage: "start single runner",
Action: runSingle,
func init() {
common.RegisterCommand(cli.Command{
Name: "run-single",
Usage: "start single runner",
Action: runSingle,
Flags: []cli.Flag{
cli.StringFlag{
Name: "token",
......@@ -142,5 +141,5 @@ var (
EnvVar: "RUNNER_BUILDS_DIR",
},
},
}
)
})
}
package common
import (
log "github.com/Sirupsen/logrus"
"github.com/codegangsta/cli"
)
var commands []cli.Command
func RegisterCommand(command cli.Command) {
log.Debugln("Registering", command.Name, "command...")
commands = append(commands, command)
}
func GetCommands() []cli.Command {
return commands
}
......@@ -56,7 +56,6 @@ type RunnerConfig struct {
type BaseConfig struct {
Concurrent int `toml:"concurrent" json:"concurrent"`
RootDir string `toml:"root_dir" json:"root_dir"`
Runners []*RunnerConfig `toml:"runners" json:"runners"`
}
......@@ -110,12 +109,3 @@ func (c *Config) SaveConfig(configFile string) error {
return nil
}
func (c *Config) SetChdir() {
if len(c.RootDir) > 0 {
err := os.Chdir(c.RootDir)
if err != nil {
panic(err)
}
}
}
......@@ -8,6 +8,7 @@ import (
"github.com/codegangsta/cli"
"github.com/ayufan/gitlab-ci-multi-runner/commands"
"github.com/ayufan/gitlab-ci-multi-runner/common"
_ "github.com/ayufan/gitlab-ci-multi-runner/executors/docker"
_ "github.com/ayufan/gitlab-ci-multi-runner/executors/parallels"
_ "github.com/ayufan/gitlab-ci-multi-runner/executors/shell"
......@@ -53,11 +54,7 @@ func main() {
return nil
}
app.Commands = []cli.Command{
commands.CmdRunMulti,
commands.CmdRunSetup,
commands.CmdRunSingle,
}
app.Commands = common.GetCommands()
if err := app.Run(os.Args); err != nil {
log.Fatal(err)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment