diff --git a/cmd/coord-server/main.go b/cmd/coord-server/main.go index 73b8afea5edb7734a158a5c1ad3707b24b2000ba..9bde32cbe5dcf9f71a7eaf0b06d875da11ab0deb 100644 --- a/cmd/coord-server/main.go +++ b/cmd/coord-server/main.go @@ -10,33 +10,96 @@ import ( "syscall" "time" + "github.com/mkideal/cli" + "gitlab.irstea.fr/guillaume.perreal/coord/lib/safe" ) +type TCPAddr struct { + *net.TCPAddr +} + +func (t *TCPAddr) Decode(s string) (err error) { + t.TCPAddr, err = net.ResolveTCPAddr("tcp", s) + return +} + +type UnixAddr struct { + *net.UnixAddr +} + +func (t *UnixAddr) Decode(s string) (err error) { + t.UnixAddr, err = net.ResolveUnixAddr("unix", s) + return +} + +type LoggerConfig struct { + Verbose []bool `cli:"v,verbose" usage:"increase the log verbosity"` + Quiet bool `cli:"q,quiet" usage:"decrease the log verbosity"` +} + +type serverConfig struct { + cli.Helper + TCP []TCPAddr `cli:"t,tcp" usage:"serve on TCP port"` + Unix []UnixAddr `cli:"u,unix" usage:"serve on unix socket"` + Console bool `cli:"C,console" usage:"open a connection to the console"` + LoggerConfig +} + func main() { - var stop safe.StopOnce - defer stop.Stop() + os.Exit(cli.Run(new(serverConfig), func(ctx *cli.Context) error { + conf := ctx.Argv().(*serverConfig) + return runServer(*conf) + })) +} + +func runServer(conf serverConfig) (err error) { + defer log.Info("exiting") + + setupLogger(conf.LoggerConfig) + + log.Debugf("configuration: %+v", conf) stream := NewStream() - stop.BindStopper(stream) - startConsoleConnection(stream) + if conf.Console { + startConsoleConnection(stream) + } - if err := startUnixListener(stream); err != nil { - log.Errorf("could not start socket listener: %s", err) + for _, unixAddr := range conf.Unix { + if err := startUnixListener(stream, unixAddr.UnixAddr); err != nil { + log.Errorf("could not start socket listener: %s", err) + } } - if err := startTCPListener(stream); err != nil { - log.Errorf("could not start TCP listener: %s", err) + + for _, tcpAddr := range conf.TCP { + if err := startTCPListener(stream, tcpAddr.TCPAddr); err != nil { + log.Errorf("could not start TCP listener: %s", err) + } } signals := make(chan os.Signal, 1) signal.Notify(signals, syscall.SIGTERM, syscall.SIGINT) - select { - case <-stop.Stopped(): - case sig := <-signals: - log.Criticalf("received signal '%s', exiting", sig) + sig := <-signals + log.Criticalf("received signal '%s', shutting down", sig) + stream.Stop() + + return +} + +func setupLogger(conf LoggerConfig) { + level := log.NOTICE + if conf.Quiet { + level = log.ERROR + } else { + switch len(conf.Verbose) { + case 0: level = log.NOTICE + case 1: level = log.INFO + default: level = log.DEBUG + } } + log.DefaultLogger = log.NewLogger(log.NewLogWriter(level, os.Stderr, log.TextFormatter)) } func startConsoleConnection(stream *Stream) { @@ -48,12 +111,7 @@ func startConsoleConnection(stream *Stream) { ) } -func startUnixListener(stream *Stream) error { - addr, err := net.ResolveUnixAddr("unix", ".conn") - if err != nil { - return err - } - +func startUnixListener(stream *Stream, addr *net.UnixAddr) error { listener, err := net.ListenUnix("unix", addr) if err != nil { return err @@ -61,45 +119,50 @@ func startUnixListener(stream *Stream) error { listener.SetUnlinkOnClose(true) logger := log.DefaultLogger.WithField("server", fmt.Sprintf("%s://%s", addr.Net, addr.Name)) - safe.Go(func() { runListener(stream, listener, logger) }) - return nil + return runListener(stream, listener, logger) } -func startTCPListener(stream *Stream) error { - addr, err := net.ResolveTCPAddr("tcp", "0.0.0.0:8650") - if err != nil { - return err - } - +func startTCPListener(stream *Stream, addr *net.TCPAddr) error { listener, err := net.ListenTCP("tcp", addr) if err != nil { return err } logger := log.DefaultLogger.WithField("server", fmt.Sprintf("%s://%s:%d", addr.Network(), addr.IP, addr.Port)) - safe.Go(func() { runListener(stream, listener, logger) }) - return nil + return runListener(stream, listener, logger) } -func runListener(stream *Stream, listener net.Listener, logger *log.Logger) { - defer logger.Noticef("closed") - logger.Noticef("accepting connections") +func runListener(stream *Stream, listener net.Listener, logger *log.Logger) error { + stream.BindCloser(listener) - for { - if subConn, err := waitNewConnection(listener); err == nil { - subLogger := logger.WithField("client", subConn.RemoteAddr()) - NewConnection(subConn, stream, subLogger) - } else if netErr, ok := err.(net.Error); ok { - if netErr.Timeout() { - continue + safe.Go(func() { + defer func() { + if err := listener.Close(); err != nil { + logger.Noticef("error closing the listener: %s", err) + } else { + logger.Noticef("closed") } - if !netErr.Temporary() { - logger.Errorf("%s", err) - return + }() + logger.Noticef("accepting connections") + + for { + if subConn, err := waitNewConnection(listener); err == nil { + subLogger := logger.WithField("client", subConn.RemoteAddr()) + NewConnection(subConn, stream, subLogger) + } else if netErr, ok := err.(net.Error); ok { + if netErr.Timeout() { + continue + } + if !netErr.Temporary() { + logger.Errorf("%s", err) + return + } + logger.Warningf("%s", err) } - logger.Warningf("%s", err) } - } + }) + + return nil } type SetDeadliner interface { diff --git a/go.mod b/go.mod index ac345c59c701f27f3638217aece5d7cde6a7175f..bb41a8d2153b41e4ead95a733432478d5b07b472 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,12 @@ module gitlab.irstea.fr/guillaume.perreal/coord go 1.12 require ( + github.com/Bowery/prompt v0.0.0-20190419144237-972d0ceb96f5 // indirect github.com/google/uuid v1.1.1 github.com/joomcode/errorx v0.8.0 + github.com/labstack/gommon v0.2.9 // indirect + github.com/mkideal/cli v0.0.3 + github.com/mkideal/pkg v0.0.0-20170503154153-3e188c9e7ecc // indirect github.com/stretchr/testify v1.3.0 + golang.org/x/crypto v0.0.0-20190618222545-ea8f1a30c443 // indirect ) diff --git a/go.sum b/go.sum index 1fba44306de43e09907a512ae09db1e9af196a3d..bb09fed6c61e7d1b3344a1836c8171228d1b5a06 100644 --- a/go.sum +++ b/go.sum @@ -1,12 +1,39 @@ +github.com/Bowery/prompt v0.0.0-20190419144237-972d0ceb96f5 h1:7tNlRGC3pUEPKS3DwgX5L0s+cBloaq/JBoi9ceN1MCM= +github.com/Bowery/prompt v0.0.0-20190419144237-972d0ceb96f5/go.mod h1:4/6eNcqZ09BZ9wLK3tZOjBA1nDj+B0728nlX5YRlSmQ= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/joomcode/errorx v0.8.0 h1:GhAqPtcYuo1O7TOIbtzEIDzPGQ3SrKJ3tdjXNmUtDNo= github.com/joomcode/errorx v0.8.0/go.mod h1:kgco15ekB6cs+4Xjzo7SPeXzx38PbJzBwbnu9qfVNHQ= +github.com/labstack/gommon v0.2.9 h1:heVeuAYtevIQVYkGj6A41dtfT91LrvFG220lavpWhrU= +github.com/labstack/gommon v0.2.9/go.mod h1:E8ZTmW9vw5az5/ZyHWCp0Lw4OH2ecsaBP1C/NKavGG4= +github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mkideal/cli v0.0.3 h1:Y1OXyfTVI9eQ9RTiXq12h7q88y22Q9ZU4VI09ifz6lE= +github.com/mkideal/cli v0.0.3/go.mod h1:HLuSls75T7LFlTgByGeuLwcvdUmmx/aUQxnnEKxoZzY= +github.com/mkideal/pkg v0.0.0-20170503154153-3e188c9e7ecc h1:eyN9UWVX+CeeCQZPudCUAPc84xQYTjEu9MWNa2HuJrs= +github.com/mkideal/pkg v0.0.0-20170503154153-3e188c9e7ecc/go.mod h1:DECgB56amjU/mmmsKuooNPQ1856HASOMC3D4ntSVU70= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190618222545-ea8f1a30c443 h1:IcSOAf4PyMp3U3XbIEj1/xJ2BjNN2jWv7JoyOsMxXUU= +golang.org/x/crypto v0.0.0-20190618222545-ea8f1a30c443/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed h1:uPxWBzB3+mlnjy9W58qY1j/cjyFjutgw/Vhan2zLy/A= +golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/lib/log/default.go b/lib/log/default.go index 1f762d2454f5dd11534eb516966a33d3b08816be..97b4f34a3e817af3c626028df365c18a2d923c29 100644 --- a/lib/log/default.go +++ b/lib/log/default.go @@ -5,7 +5,7 @@ import ( ) // DefaultLogger is the package logger. -var DefaultLogger = &Logger{NewLogWriter(DEBUG, os.Stderr, TextFormatter)} +var DefaultLogger = NewLogger(NewLogWriter(DEBUG, os.Stderr, TextFormatter)) // Debug logs a message at DEBUG level using the package logger. func Debug(message string) { DefaultLogger.Debug(message) } diff --git a/lib/log/logger.go b/lib/log/logger.go index 40586d8412faf06965889906240f1e78b4a04730..669621241e3c7a8e491b26c433d349dc5b2822e1 100644 --- a/lib/log/logger.go +++ b/lib/log/logger.go @@ -16,6 +16,10 @@ func (f RawLoggerFunc) Log(entry *Entry) { f(entry) } // Logger adds convenience methods to a RawLogger. type Logger struct{ inner RawLogger } +func NewLogger(inner RawLogger) *Logger { + return &Logger{inner} +} + func (l *Logger) Log(entry *Entry) { l.inner.Log(entry) } func (l *Logger) Debug(message string) { l.inner.Log(NewEntry(DEBUG, message)) }