micro/runtime/service.go

179 lines
2.9 KiB
Go
Raw Normal View History

package runtime
import (
"io"
"sync"
"time"
2019-11-19 16:09:43 +00:00
"github.com/micro/go-micro/runtime/build"
"github.com/micro/go-micro/runtime/process"
proc "github.com/micro/go-micro/runtime/process/os"
"github.com/micro/go-micro/util/log"
)
type service struct {
sync.RWMutex
running bool
closed chan bool
err error
updated time.Time
// output for logs
output io.Writer
// service to manage
*Service
// process creator
Process *proc.Process
// Exec
Exec *process.Executable
// process pid
PID *process.PID
}
func newService(s *Service, c CreateOptions) *service {
var exec string
var args []string
2019-11-29 11:35:00 +00:00
// set command
exec = c.Command[0]
// set args
if len(c.Command) > 1 {
args = c.Command[1:]
}
return &service{
Service: s,
Process: new(proc.Process),
Exec: &process.Executable{
2019-11-19 16:09:43 +00:00
Package: &build.Package{
Name: s.Name,
Path: exec,
},
Env: c.Env,
Args: args,
},
closed: make(chan bool),
output: c.Output,
updated: time.Now(),
}
}
func (s *service) streamOutput() {
go io.Copy(s.output, s.PID.Output)
go io.Copy(s.output, s.PID.Error)
}
// Running returns true is the service is running
func (s *service) Running() bool {
s.RLock()
defer s.RUnlock()
return s.running
}
// Start stars the service
func (s *service) Start() error {
s.Lock()
defer s.Unlock()
if s.running {
return nil
}
// reset
s.err = nil
s.closed = make(chan bool)
2020-01-10 21:54:28 +00:00
if s.Metadata == nil {
s.Metadata = make(map[string]string)
}
s.Metadata["status"] = "starting"
// delete any existing error
delete(s.Metadata, "error")
// TODO: pull source & build binary
log.Debugf("Runtime service %s forking new process", s.Service.Name)
p, err := s.Process.Fork(s.Exec)
if err != nil {
2020-01-10 21:54:28 +00:00
s.Metadata["status"] = "error"
s.Metadata["error"] = err.Error()
return err
}
// set the pid
s.PID = p
// set to running
s.running = true
2020-01-10 21:54:28 +00:00
// set status
s.Metadata["status"] = "running"
if s.output != nil {
s.streamOutput()
}
// wait and watch
go s.Wait()
return nil
}
// Stop stops the service
func (s *service) Stop() error {
s.Lock()
defer s.Unlock()
select {
case <-s.closed:
return nil
default:
close(s.closed)
s.running = false
if s.PID == nil {
return nil
}
2020-01-10 21:54:28 +00:00
// set status
s.Metadata["status"] = "stopping"
// kill the process
err := s.Process.Kill(s.PID)
// wait for it to exit
s.Process.Wait(s.PID)
2020-01-10 21:54:28 +00:00
// set status
s.Metadata["status"] = "stopped"
// return the kill error
return err
}
}
// Error returns the last error service has returned
func (s *service) Error() error {
s.RLock()
defer s.RUnlock()
return s.err
}
// Wait waits for the service to finish running
func (s *service) Wait() {
// wait for process to exit
err := s.Process.Wait(s.PID)
s.Lock()
defer s.Unlock()
// save the error
if err != nil {
2020-01-10 21:54:28 +00:00
s.Metadata["status"] = "error"
s.Metadata["error"] = err.Error()
s.err = err
}
// no longer running
s.running = false
}