Add support for OpenRC services (alpine/busybox) (#252)
This commit is contained in:
+12
-2
@@ -1,8 +1,9 @@
|
|||||||
|
|
||||||
all: sysv systemd upstart clean
|
all: sysv systemd upstart openrc clean
|
||||||
|
|
||||||
|
# compile `go test` binary statically
|
||||||
test:
|
test:
|
||||||
@go test -c ..
|
@CGO_ENABLED=0 go test -installsuffix netgo -a -c ..
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
-rm service.test
|
-rm service.test
|
||||||
@@ -33,3 +34,12 @@ upstart: test
|
|||||||
@-docker rm $(shell docker ps -l -q)
|
@-docker rm $(shell docker ps -l -q)
|
||||||
@-docker rmi -f service.test.upstart
|
@-docker rmi -f service.test.upstart
|
||||||
@-rm upstart/service.test
|
@-rm upstart/service.test
|
||||||
|
|
||||||
|
openrc: test
|
||||||
|
@echo openrc
|
||||||
|
@cp service.test openrc/
|
||||||
|
@docker build -q --tag="service.test.openrc" openrc
|
||||||
|
@-docker run service.test.openrc
|
||||||
|
@-docker rm $(shell docker ps -l -q)
|
||||||
|
@-docker rmi -f service.test.openrc
|
||||||
|
@-rm openrc/service.test
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
FROM alpine:latest
|
||||||
|
ADD service.test /tmp/
|
||||||
|
CMD /tmp/service.test -test.v=true
|
||||||
@@ -93,6 +93,7 @@ const (
|
|||||||
optionSysvScript = "SysvScript"
|
optionSysvScript = "SysvScript"
|
||||||
optionUpstartScript = "UpstartScript"
|
optionUpstartScript = "UpstartScript"
|
||||||
optionLaunchdConfig = "LaunchdConfig"
|
optionLaunchdConfig = "LaunchdConfig"
|
||||||
|
optionOpenRCScript = "OpenRCScript"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Status represents service status as an byte value
|
// Status represents service status as an byte value
|
||||||
|
|||||||
@@ -53,6 +53,15 @@ func init() {
|
|||||||
},
|
},
|
||||||
new: newUpstartService,
|
new: newUpstartService,
|
||||||
},
|
},
|
||||||
|
linuxSystemService{
|
||||||
|
name: "linux-openrc",
|
||||||
|
detect: isOpenRC,
|
||||||
|
interactive: func() bool {
|
||||||
|
is, _ := isInteractive()
|
||||||
|
return is
|
||||||
|
},
|
||||||
|
new: newOpenRCService,
|
||||||
|
},
|
||||||
linuxSystemService{
|
linuxSystemService{
|
||||||
name: "unix-systemv",
|
name: "unix-systemv",
|
||||||
detect: func() bool { return true },
|
detect: func() bool { return true },
|
||||||
|
|||||||
@@ -0,0 +1,226 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"os/signal"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
"text/template"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func isOpenRC() bool {
|
||||||
|
if _, err := exec.LookPath("openrc-init"); err == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if _, err := os.Stat("/etc/inittab"); err == nil {
|
||||||
|
filerc, err := os.Open("/etc/inittab")
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
defer filerc.Close()
|
||||||
|
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
buf.ReadFrom(filerc)
|
||||||
|
contents := buf.String()
|
||||||
|
|
||||||
|
re := regexp.MustCompile(`::sysinit:.*openrc.*sysinit`)
|
||||||
|
matches := re.FindStringSubmatch(contents)
|
||||||
|
if len(matches) > 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
type openrc struct {
|
||||||
|
i Interface
|
||||||
|
platform string
|
||||||
|
*Config
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *openrc) String() string {
|
||||||
|
if len(s.DisplayName) > 0 {
|
||||||
|
return s.DisplayName
|
||||||
|
}
|
||||||
|
return s.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *openrc) Platform() string {
|
||||||
|
return s.platform
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *openrc) template() *template.Template {
|
||||||
|
customScript := s.Option.string(optionOpenRCScript, "")
|
||||||
|
|
||||||
|
if customScript != "" {
|
||||||
|
return template.Must(template.New("").Funcs(tf).Parse(customScript))
|
||||||
|
} else {
|
||||||
|
return template.Must(template.New("").Funcs(tf).Parse(openRCScript))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newOpenRCService(i Interface, platform string, c *Config) (Service, error) {
|
||||||
|
s := &openrc{
|
||||||
|
i: i,
|
||||||
|
platform: platform,
|
||||||
|
Config: c,
|
||||||
|
}
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var errNoUserServiceOpenRC = errors.New("user services are not supported on OpenRC")
|
||||||
|
|
||||||
|
func (s *openrc) configPath() (cp string, err error) {
|
||||||
|
if s.Option.bool(optionUserService, optionUserServiceDefault) {
|
||||||
|
err = errNoUserServiceOpenRC
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cp = "/etc/init.d/" + s.Config.Name
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *openrc) Install() error {
|
||||||
|
confPath, err := s.configPath()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = os.Stat(confPath)
|
||||||
|
if err == nil {
|
||||||
|
return fmt.Errorf("Init already exists: %s", confPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := os.Create(confPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
err = os.Chmod(confPath, 0755)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
path, err := s.execPath()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var to = &struct {
|
||||||
|
*Config
|
||||||
|
Path string
|
||||||
|
}{
|
||||||
|
s.Config,
|
||||||
|
path,
|
||||||
|
}
|
||||||
|
|
||||||
|
err = s.template().Execute(f, to)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// run rc-update
|
||||||
|
return s.runAction("add")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *openrc) Uninstall() error {
|
||||||
|
confPath, err := s.configPath()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := os.Remove(confPath); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return s.runAction("delete")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *openrc) Logger(errs chan<- error) (Logger, error) {
|
||||||
|
if system.Interactive() {
|
||||||
|
return ConsoleLogger, nil
|
||||||
|
}
|
||||||
|
return s.SystemLogger(errs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *openrc) SystemLogger(errs chan<- error) (Logger, error) {
|
||||||
|
return newSysLogger(s.Name, errs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *openrc) Run() (err error) {
|
||||||
|
err = s.i.Start(s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Option.funcSingle(optionRunWait, func() {
|
||||||
|
var sigChan = make(chan os.Signal, 3)
|
||||||
|
signal.Notify(sigChan, syscall.SIGTERM, os.Interrupt)
|
||||||
|
<-sigChan
|
||||||
|
})()
|
||||||
|
|
||||||
|
return s.i.Stop(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *openrc) Status() (Status, error) {
|
||||||
|
_, out, err := runWithOutput("rc-service", s.Name, "status")
|
||||||
|
if err != nil {
|
||||||
|
return StatusUnknown, err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case strings.HasPrefix(out, "Running"):
|
||||||
|
return StatusRunning, nil
|
||||||
|
case strings.HasPrefix(out, "Stopped"):
|
||||||
|
return StatusStopped, nil
|
||||||
|
default:
|
||||||
|
return StatusUnknown, ErrNotInstalled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *openrc) Start() error {
|
||||||
|
return run("rc-service", s.Name, "start")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *openrc) Stop() error {
|
||||||
|
return run("rc-service", s.Name, "stop")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *openrc) Restart() error {
|
||||||
|
err := s.Stop()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
time.Sleep(50 * time.Millisecond)
|
||||||
|
return s.Start()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *openrc) runAction(action string) error {
|
||||||
|
return s.run(action, s.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *openrc) run(action string, args ...string) error {
|
||||||
|
return run("rc-update", append([]string{action}, args...)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
const openRCScript = `#!/sbin/openrc-run
|
||||||
|
supervisor=supervise-daemon
|
||||||
|
name="{{.DisplayName}}"
|
||||||
|
description="{{.Description}}"
|
||||||
|
command={{.Path|cmdEscape}}
|
||||||
|
{{- if .Arguments }}
|
||||||
|
command_args="{{range .Arguments}}{{.}} {{end}}"
|
||||||
|
{{- end }}
|
||||||
|
name=$(basename $(readlink -f $command))
|
||||||
|
supervise_daemon_args="--stdout /var/log/${name}.log --stderr /var/log/${name}.err"
|
||||||
|
|
||||||
|
{{- if .Dependencies }}
|
||||||
|
depend() {
|
||||||
|
{{- range $i, $dep := .Dependencies}}
|
||||||
|
{{"\t"}}{{$dep}}{{end}}
|
||||||
|
}
|
||||||
|
{{- end}}
|
||||||
|
`
|
||||||
Reference in New Issue
Block a user