Skip to content

Commit

Permalink
Merge branch 'release/0.8.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
nbari committed Sep 3, 2016
2 parents f2e1dcf + 640ed50 commit cc8ba3c
Show file tree
Hide file tree
Showing 5 changed files with 169 additions and 41 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
[![Build Status](https://travis-ci.org/immortal/immortal.svg?branch=develop)](https://travis-ci.org/immortal/immortal)
[![Coverage Status](https://coveralls.io/repos/github/immortal/immortal/badge.svg?branch=develop)](https://coveralls.io/github/immortal/immortal?branch=develop)

CLI tools for supervising and running commands.
A *nix cross-platform (OS agnostic) supervisor

https://immortal.run/

[ ![Download](https://api.bintray.com/packages/nbari/immortal/immortal/images/download.svg) ](https://bintray.com/nbari/immortal/immortal/_latestVersion)
37 changes: 16 additions & 21 deletions info.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,10 @@ import (
"time"
)

func (d *Daemon) Info(ch <-chan os.Signal) {
func (d *Daemon) Info() {
var m runtime.MemStats
for {
select {
case <-ch:
runtime.ReadMemStats(&m)
status := `PID: %d
runtime.ReadMemStats(&m)
status := `PID: %d
Gorutines: %d
Alloc : %d
Total Alloc: %d
Expand All @@ -25,19 +22,17 @@ Seconds in GC: %d
Started on: %v
Uptime: %v
Process count: %d`
log.Printf(status,
os.Getpid(),
runtime.NumGoroutine(),
m.Alloc,
m.TotalAlloc,
m.Sys,
m.Lookups,
m.Mallocs,
m.Frees,
m.PauseTotalNs/1000000000,
d.sTime.Format(time.RFC3339),
time.Since(d.sTime),
d.count)
}
}
log.Printf(status,
os.Getpid(),
runtime.NumGoroutine(),
m.Alloc,
m.TotalAlloc,
m.Sys,
m.Lookups,
m.Mallocs,
m.Frees,
m.PauseTotalNs/1000000000,
d.sTime.Format(time.RFC3339),
time.Since(d.sTime),
d.count)
}
5 changes: 3 additions & 2 deletions supervise.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,8 @@ func Supervise(d *Daemon) {
log.Fatal(err)
}

// Info loop kill 3 pid get stats
// Info loop, kill 3 PPID get stats
signal.Notify(info, syscall.SIGQUIT)
go d.Info(info)

// create a supervisor
s := &Sup{p}
Expand All @@ -41,6 +40,8 @@ func Supervise(d *Daemon) {
select {
case <-d.quit:
return
case <-info:
d.Info()
case <-run:
time.Sleep(wait)
// create a new process
Expand Down
139 changes: 139 additions & 0 deletions supervise_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
package immortal

import (
"fmt"
"io/ioutil"
"log"
"os"
"os/exec"
"os/signal"
"path/filepath"
"strconv"
"syscall"
"testing"
"time"
)

func TestHelperProcessSupervise(*testing.T) {
if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
return
}
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt, os.Kill)
select {
case <-c:
os.Exit(1)
case <-time.After(10 * time.Second):
os.Exit(0)
}
}

func TestSupervise(t *testing.T) {
log.SetOutput(ioutil.Discard)
log.SetFlags(0)
base := filepath.Base(os.Args[0]) // "exec.test"
dir := filepath.Dir(os.Args[0]) // "/tmp/go-buildNNNN/os/exec/_test"
if dir == "." {
t.Skip("skipping; running test at root somehow")
}
parentDir := filepath.Dir(dir) // "/tmp/go-buildNNNN/os/exec"
dirBase := filepath.Base(dir) // "_test"
if dirBase == "." {
t.Skipf("skipping; unexpected shallow dir of %q", dir)
}
tmpfile, err := ioutil.TempFile("", "TestPidFile")
if err != nil {
t.Error(err)
}
defer os.Remove(tmpfile.Name()) // clean up
cfg := &Config{
Env: map[string]string{"GO_WANT_HELPER_PROCESS": "1"},
command: []string{filepath.Join(dirBase, base), "-test.run=TestHelperProcessSupervise", "--"},
Cwd: parentDir,
ctrl: true,
Pid: Pid{
Parent: filepath.Join(parentDir, "parent.pid"),
Child: filepath.Join(parentDir, "child.pid"),
Follow: tmpfile.Name(),
},
}
// to remove lock
os.RemoveAll(filepath.Join(parentDir, "supervise"))
d, err := New(cfg)
if err != nil {
t.Fatal(err)
}
time.Sleep(time.Second)
fctrl, err := OpenFifo(filepath.Join(parentDir, "supervise/control"))
if err != nil {
t.Fatal(err)
}
go Supervise(d)
defer func() {
fmt.Fprintln(fctrl, "kill")
fmt.Fprintln(fctrl, "exit")
}()

sup := &Sup{}

time.Sleep(time.Second)

// check pids
parent_pid, err := sup.ReadPidFile(filepath.Join(parentDir, "parent.pid"))
if err != nil {
t.Error(err)
}
expect(t, os.Getpid(), parent_pid)
child_pid, err := sup.ReadPidFile(filepath.Join(parentDir, "child.pid"))
if err != nil {
t.Error(err)
}
expect(t, true, child_pid > 0)

fmt.Fprintln(fctrl, "t")
time.Sleep(time.Second)
newchild_pid, err := sup.ReadPidFile(filepath.Join(parentDir, "child.pid"))
if err != nil {
t.Error(err)
}
if child_pid == newchild_pid {
t.Error("Expecting new child pid")
}

// test info
syscall.Kill(parent_pid, syscall.SIGQUIT)
time.Sleep(time.Second)

// fake watch pid with other process
cmd := exec.Command("sleep", "1")
cmd.Start()
go func() {
cmd.Wait()
}()
watchPid := cmd.Process.Pid
err = ioutil.WriteFile(tmpfile.Name(), []byte(strconv.Itoa(watchPid)), 0644)
if err != nil {
t.Error(err)
}

// reset
fmt.Fprintln(fctrl, "t")

select {
case <-time.After(5 * time.Second):
t.Error("time out")
default:
for sup.IsRunning(watchPid) {
// wait mock watchpid to finish
time.Sleep(1500 * time.Millisecond)
fmt.Printf("sup.IsRunning(watchPid) = %+v %d\n", sup.IsRunning(watchPid), watchPid)
}
newchild_pid_after, err := sup.ReadPidFile(filepath.Join(parentDir, "child.pid"))
if err != nil {
t.Error(err)
}
if newchild_pid == newchild_pid_after {
t.Error("Expecting different pids")
}
}
}
25 changes: 8 additions & 17 deletions supervisor.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package immortal

import (
"bufio"
"io"
"io/ioutil"
"os"
"strconv"
Expand All @@ -24,7 +23,7 @@ type Sup struct {
}

func (self *Sup) IsRunning(pid int) bool {
process, _ := os.FindProcess(int(pid))
process, _ := os.FindProcess(pid)
if err := process.Signal(syscall.Signal(0)); err != nil {
return false
}
Expand All @@ -48,25 +47,17 @@ func (self *Sup) ReadPidFile(pidfile string) (int, error) {
func (self *Sup) ReadFifoControl(fifo *os.File, ch chan<- Return) {
r := bufio.NewReader(fifo)

buf := make([]byte, 0, 8)

go func() {
defer fifo.Close()
for {
n, err := r.Read(buf[:cap(buf)])
if n == 0 {
if err == nil {
continue
}
if err == io.EOF {
continue
}
s, err := r.ReadString('\n')
if err != nil {
ch <- Return{err: err, msg: ""}
}
buf = buf[:n]
ch <- Return{
err: nil,
msg: strings.ToLower(strings.TrimSpace(string(buf))),
} else {
ch <- Return{
err: nil,
msg: strings.ToLower(strings.TrimSpace(s)),
}
}
}
}()
Expand Down

0 comments on commit cc8ba3c

Please sign in to comment.