Break Service interface into smaller chunks. Add Start and Stop methods.
This commit is contained in:
+28
-9
@@ -6,11 +6,15 @@ import (
|
||||
"os"
|
||||
)
|
||||
|
||||
var localLog service.Logger
|
||||
|
||||
func main() {
|
||||
var name = "GoServiceTest"
|
||||
var displayName = "Go Service Test"
|
||||
var desc = "This is a test Go service. It is designed to run well."
|
||||
var ws, err = service.NewService(name, displayName, desc)
|
||||
|
||||
var s, err = service.NewService(name, displayName, desc)
|
||||
localLog = s
|
||||
|
||||
if err != nil {
|
||||
fmt.Printf("%s unable to start: %s", displayName, err)
|
||||
@@ -22,41 +26,56 @@ func main() {
|
||||
verb := os.Args[1]
|
||||
switch verb {
|
||||
case "install":
|
||||
err = ws.Install()
|
||||
err = s.Install()
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to install: %s\n", err)
|
||||
return
|
||||
}
|
||||
fmt.Printf("Service \"%s\" installed.\n", displayName)
|
||||
case "remove":
|
||||
err = ws.Remove()
|
||||
err = s.Remove()
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to remove: %s\n", err)
|
||||
return
|
||||
}
|
||||
fmt.Printf("Service \"%s\" removed.\n", displayName)
|
||||
case "run":
|
||||
doWork()
|
||||
case "start":
|
||||
err = s.Start()
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to start: %s\n", err)
|
||||
return
|
||||
}
|
||||
fmt.Printf("Service \"%s\" started.\n", displayName)
|
||||
case "stop":
|
||||
err = s.Stop()
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to stop: %s\n", err)
|
||||
return
|
||||
}
|
||||
fmt.Printf("Service \"%s\" stopped.\n", displayName)
|
||||
}
|
||||
return
|
||||
}
|
||||
err = ws.Run(func() error {
|
||||
err = s.Run(func() error {
|
||||
// start
|
||||
go doWork()
|
||||
ws.LogInfo("I'm Running!")
|
||||
return nil
|
||||
}, func() error {
|
||||
// stop
|
||||
stopWork()
|
||||
ws.LogInfo("I'm Stopping!")
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
ws.LogError(err.Error())
|
||||
s.LogError(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func doWork() {
|
||||
|
||||
localLog.LogInfo("I'm Running!")
|
||||
select {}
|
||||
}
|
||||
func stopWork() {
|
||||
|
||||
localLog.LogInfo("I'm Stopping!")
|
||||
}
|
||||
|
||||
+32
-8
@@ -1,5 +1,5 @@
|
||||
// Package service provides a simple way to create a system service.
|
||||
// Currently supports Windows and Linux/Upstart.
|
||||
// Currently supports Windows, Linux/Upstart, OSX/Launchd.
|
||||
package service
|
||||
|
||||
// Creates a new service. name is the internal name
|
||||
@@ -12,14 +12,14 @@ func NewService(name, displayName, description string) (Service, error) {
|
||||
|
||||
// Represents a generic way to interact with the system's service.
|
||||
type Service interface {
|
||||
// Installs this service on the system. May return an
|
||||
// error if this service is already installed.
|
||||
Install() error
|
||||
|
||||
// Removes this service from the system. May return an
|
||||
// error if this service is not already installed.
|
||||
Remove() error
|
||||
Installer
|
||||
Controller
|
||||
Runner
|
||||
Logger
|
||||
}
|
||||
|
||||
// A Generic way to stop and start a service.
|
||||
type Runner interface {
|
||||
// Call quickly after initial entry point. Does not return until
|
||||
// service is ready to stop. onStart is called when the service is
|
||||
// starting, returning an error will fail to start the service.
|
||||
@@ -28,7 +28,31 @@ type Service interface {
|
||||
// an error from Run.
|
||||
// Both callbacks should return quickly and not block.
|
||||
Run(onStart, onStop func() error) error
|
||||
}
|
||||
|
||||
// Simple install and remove commands.
|
||||
type Installer interface {
|
||||
// Installs this service on the system. May return an
|
||||
// error if this service is already installed.
|
||||
Install() error
|
||||
|
||||
// Removes this service from the system. May return an
|
||||
// error if this service is not already installed.
|
||||
Remove() error
|
||||
}
|
||||
|
||||
// A service that implements ServiceController is able to
|
||||
// start and stop itself.
|
||||
type Controller interface {
|
||||
// Starts this service on the system.
|
||||
Start() error
|
||||
|
||||
// Stops this service on the system.
|
||||
Stop() error
|
||||
}
|
||||
|
||||
// A service that implements ServiceLogger can perform simple system logging.
|
||||
type Logger interface {
|
||||
// Basic log functions in the context of the service.
|
||||
LogError(format string, a ...interface{}) error
|
||||
LogWarning(format string, a ...interface{}) error
|
||||
|
||||
+22
-25
@@ -5,8 +5,8 @@ package service
|
||||
#include <string.h>
|
||||
|
||||
int
|
||||
GetExecPath(char* path) {
|
||||
uint32_t size = 32*1024;
|
||||
GetExecPath(char* path, int bufferSize) {
|
||||
uint32_t size = bufferSize;
|
||||
if (_NSGetExecutablePath(path, &size) == 0) {
|
||||
// Despite Apple docs, size does NOT get set in call.
|
||||
return strlen(path);
|
||||
@@ -28,7 +28,8 @@ import (
|
||||
"text/template"
|
||||
)
|
||||
|
||||
// BUG(kardia): I have not confirmed this service works as intended on launchd.
|
||||
const maxPathSize = 32 * 1024
|
||||
|
||||
func newService(name, displayName, description string) (s *darwinLaunchdService, err error) {
|
||||
s = &darwinLaunchdService{
|
||||
name: name,
|
||||
@@ -83,30 +84,26 @@ func (s *darwinLaunchdService) Install() error {
|
||||
path,
|
||||
}
|
||||
|
||||
t := template.Must(template.New("upstartScript").Parse(upstartScript))
|
||||
err = t.Execute(f, to)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd := exec.Command("launchctl", "load", confPath)
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
t := template.Must(template.New("launchdConfig").Parse(launchdConfig))
|
||||
return t.Execute(f, to)
|
||||
}
|
||||
|
||||
func (s *darwinLaunchdService) Remove() error {
|
||||
s.Stop()
|
||||
|
||||
confPath := s.getServiceFilePath()
|
||||
return os.Remove(confPath)
|
||||
}
|
||||
|
||||
func (s *darwinLaunchdService) Start() error {
|
||||
confPath := s.getServiceFilePath()
|
||||
cmd := exec.Command("launchctl", "load", confPath)
|
||||
return cmd.Run()
|
||||
}
|
||||
func (s *darwinLaunchdService) Stop() error {
|
||||
confPath := s.getServiceFilePath()
|
||||
cmd := exec.Command("launchctl", "unload", confPath)
|
||||
err := cmd.Run()
|
||||
|
||||
// Don't worry about his error if not found.
|
||||
os.Remove(confPath)
|
||||
return err
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
func (s *darwinLaunchdService) Run(onStart, onStop func() error) error {
|
||||
@@ -137,8 +134,8 @@ func (s *darwinLaunchdService) LogInfo(format string, a ...interface{}) error {
|
||||
}
|
||||
|
||||
func getExePath() (exePath string, err error) {
|
||||
buffer := make([]byte, 32*1024)
|
||||
size := C.GetExecPath((*C.char)(unsafe.Pointer(&buffer[0])))
|
||||
buffer := make([]byte, maxPathSize)
|
||||
size := C.GetExecPath((*C.char)(unsafe.Pointer(&buffer[0])), maxPathSize)
|
||||
if size == 0 {
|
||||
return "", errors.New("Unable to get exec path.")
|
||||
}
|
||||
@@ -147,7 +144,7 @@ func getExePath() (exePath string, err error) {
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
var upstartScript = `<?xml version='1.0' encoding='UTF-8'?>
|
||||
var launchdConfig = `<?xml version='1.0' encoding='UTF-8'?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN"
|
||||
"http://www.apple.com/DTDs/PropertyList-1.0.dtd" >
|
||||
<plist version='1.0'>
|
||||
|
||||
+15
-1
@@ -4,8 +4,10 @@ import (
|
||||
"fmt"
|
||||
"log/syslog"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"text/template"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
func newService(name, displayName, description string) (s *linuxUpstartService, err error) {
|
||||
@@ -87,6 +89,16 @@ func (s *linuxUpstartService) Run(onStart, onStop func() error) error {
|
||||
return onStop()
|
||||
}
|
||||
|
||||
func (s *linuxUpstartService) Start() error {
|
||||
cmd := exec.Command("start", s.name)
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
func (s *linuxUpstartService) Stop() error {
|
||||
cmd := exec.Command("stop", s.name)
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
func (s *linuxUpstartService) LogError(format string, a ...interface{}) error {
|
||||
return s.logger.Err(fmt.Sprintf(format, a...))
|
||||
}
|
||||
@@ -98,7 +110,9 @@ func (s *linuxUpstartService) LogInfo(format string, a ...interface{}) error {
|
||||
}
|
||||
|
||||
func getExePath() (exePath string, err error) {
|
||||
return os.Readlink(`/proc/self/exe`)
|
||||
exePath, err = os.Readlink(`/proc/self/exe`)
|
||||
exePath = filepath.Clean(exePath)
|
||||
return
|
||||
}
|
||||
|
||||
var upstartScript = `# {{.Description}}
|
||||
|
||||
@@ -124,6 +124,37 @@ func (ws *windowsService) Run(onStart, onStop func() error) error {
|
||||
return svc.Run(ws.name, ws)
|
||||
}
|
||||
|
||||
func (ws *windowsService) Start() error {
|
||||
m, err := mgr.Connect()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer m.Disconnect()
|
||||
|
||||
s, err := m.OpenService(ws.name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer s.Close()
|
||||
return s.Start([]string{})
|
||||
}
|
||||
|
||||
func (ws *windowsService) Stop() error {
|
||||
m, err := mgr.Connect()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer m.Disconnect()
|
||||
|
||||
s, err := m.OpenService(ws.name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer s.Close()
|
||||
_, err = s.Control(svc.Stop)
|
||||
return err
|
||||
}
|
||||
|
||||
func (ws *windowsService) LogError(format string, a ...interface{}) error {
|
||||
if ws.logger == nil {
|
||||
return nil
|
||||
|
||||
Reference in New Issue
Block a user