Skip to content

Commit

Permalink
Merge pull request #3 from ramr/start_entry_pt
Browse files Browse the repository at this point in the history
Add a new entry point: Start which takes configuration
  • Loading branch information
ramr authored Aug 14, 2017
2 parents 1a6cbc0 + 456483b commit e2dea75
Show file tree
Hide file tree
Showing 7 changed files with 147 additions and 43 deletions.
61 changes: 41 additions & 20 deletions reaper.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ import "os"
import "os/signal"
import "syscall"

type Config struct {
Pid int
Options int
}

// Handle death of child (SIGCHLD) messages. Pushes the signal onto the
// notifications channel if there is a waiter.
Expand All @@ -16,9 +20,9 @@ func sigChildHandler(notifications chan os.Signal) {
signal.Notify(sigs, syscall.SIGCHLD)

for {
var sig = <- sigs
var sig = <-sigs
select {
case notifications <-sig: /* published it. */
case notifications <- sig: /* published it. */
default:
/*
* Notifications channel full - drop it to the
Expand All @@ -29,15 +33,17 @@ func sigChildHandler(notifications chan os.Signal) {
}
}

} /* End of function sigChildHandler. */

} /* End of function sigChildHandler. */

// Be a good parent - clean up behind the children.
func reapChildren() {
func reapChildren(config Config) {
var notifications = make(chan os.Signal, 1)

go sigChildHandler(notifications)

pid := config.Pid
opts := config.Options

for {
var sig = <-notifications
fmt.Printf(" - Received signal %v\n", sig)
Expand All @@ -48,9 +54,9 @@ func reapChildren() {
* Reap 'em, so that zombies don't accumulate.
* Plants vs. Zombies!!
*/
pid, err := syscall.Wait4(-1, &wstatus, 0, nil)
pid, err := syscall.Wait4(pid, &wstatus, opts, nil)
for syscall.EINTR == err {
pid, err = syscall.Wait4(-1, &wstatus, 0, nil)
pid, err = syscall.Wait4(pid, &wstatus, opts, nil)
}

if syscall.ECHILD == err {
Expand All @@ -63,31 +69,46 @@ func reapChildren() {
}
}

} /* End of function reapChildren. */


} /* End of function reapChildren. */

/*
* ======================================================================
* Section: Exported functions
* ======================================================================
*/

// Entry point for the reaper code. Start reaping children in the
// Normal entry point for the reaper code. Start reaping children in the
// background inside a goroutine.
func Reap() {
/*
* Only reap processes if we are taking over init's duties aka
* we are running as pid 1 inside a docker container.
*/
if 1 == os.Getpid() {
/*
* Ok, we are the grandma of 'em all, so we get to play
* the grim reaper.
* You will be missed, Terry Pratchett!! RIP
*/
go reapChildren()
}
if 1 == os.Getpid() {
config := Config{Pid: -1, Options: 0}

/*
* Ok, we are the grandma of 'em all, so we get to play
* the grim reaper.
* You will be missed, Terry Pratchett!! RIP
*/
go reapChildren(config)
}

} /* End of [exported] function Reap. */

} /* End of [exported] function Reap. */
// Entry point for invoking the reaper code bypassing pid 1 checks and
// with a specific configuration. Starts reaping children in the background
// inside a goroutine.
func Start(config Config) {
/*
* Start the Reaper with configuration options. This allows you to
* reap processes even if the current pid isn't running as pid 1.
* So ... use with caution!!
*
* In most cases, you are better off just using Reap() as that
* checks if we are running as Pid 1.
*/
go reapChildren(config)

} /* End of [exported] function Start. */
1 change: 1 addition & 0 deletions test/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Dockerfile
14 changes: 10 additions & 4 deletions test/Makefile
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
#!/usr/bin/env make

TEST_IMAGE = "reaper/test"
CONFIG_TEST_IMAGE = "reaper/config-test"

all: tests

test: tests

tests:
(go build testpid1.go; docker build -t reaper/test .; ./runtests.sh)



(go build testpid1.go; \
cp docker-files/no-config/Dockerfile .; \
docker build -t $(TEST_IMAGE) .; \
./runtests.sh; \
cp docker-files/json-config/Dockerfile .; \
docker build -t $(CONFIG_TEST_IMAGE) .; \
./runtests.sh "$(CONFIG_TEST_IMAGE)" )
4 changes: 4 additions & 0 deletions test/config/reaper.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"Pid": 0,
"Options": 0
}
File renamed without changes.
16 changes: 14 additions & 2 deletions test/runtests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
readonly MAX_SLEEP_TIME=$((5 + 2))
readonly IMAGE="reaper/test"

logfile="/tmp/reaper_test.log"


function get_sleepers() {
ps -ef -p $1 | grep sleep | grep -v grep
Expand Down Expand Up @@ -32,14 +34,24 @@ function check_orphans() {


function terminate_container() {
docker logs "$1" > "$logfile"
echo " - Container logs saved to $logfile"

echo " - Terminated container $(docker rm -f "$1")"

} # End of function terminate_container.


function run_tests() {
echo " - Starting docker container running image $IMAGE ..."
local elcid=$(docker run -dit $IMAGE)
local image=${1:-"${IMAGE}"}

logfile="/tmp/$(echo ${image} | sed 's#/#_#g').log"

echo " - Removing any existing log file $logfile ... "
rm -f "$logfile"

echo " - Starting docker container running image ${image} ..."
local elcid=$(docker run -dit "${image}")

echo " - Docker container name=$elcid"
local pid1=$(docker inspect --format '{{.State.Pid}}' $elcid)
Expand Down
94 changes: 77 additions & 17 deletions test/testpid1.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,53 @@
package main

import "encoding/json"
import "fmt"
import "os"
import "os/signal"
import "os/exec"
import "path/filepath"
import "syscall"
import "time"

import reaper "github.com/ramr/go-reaper"

const NWORKERS = 3
const REAPER_JSON_CONFIG = "/reaper/config/reaper.json"

const NWORKERS=3
func sleeper_test(set_proc_attributes bool) {
fmt.Printf(" - Set process attributes: %+v\n", set_proc_attributes)

cmd := exec.Command("sleep", "1")
if set_proc_attributes {
cmd.SysProcAttr = &syscall.SysProcAttr{
Setpgid: true,
Pgid: 0,
}
}

err := cmd.Start()
if err != nil {
fmt.Printf(" - Error starting sleep command: %s\n", err)
return
}

fmt.Printf("Set proc attributes: %+v\n", set_proc_attributes)

// Sleep for a wee bit longer to allow the reaper to reap the
// command on a slow system.
time.Sleep(4 * time.Second)

err = cmd.Wait()
if err != nil {
if set_proc_attributes {
fmt.Printf(" - Error waiting for command: %s\n",
err)
} else {
fmt.Printf(" - Expected wait failure: %s\n", err)
}
}

} /* End of function sleeper_test. */

func start_workers() {
// Starts up workers - which in turn start up kids that get
Expand All @@ -28,34 +64,58 @@ func start_workers() {
fmt.Printf(" - Error getting script - %s\n", scriptFile)
return
}

var args = fmt.Sprintf("%d", NWORKERS)
var cmd = exec.Command(script, args)
cmd.Start()

fmt.Printf(" - Started worker: %s %s\n", script, args)

} /* End of function start_workers. */
} /* End of function start_workers. */

func main() {
sig := make(chan os.Signal, 1)
signal.Notify(sig, syscall.SIGUSR1)

useConfig := false
config := reaper.Config{}

configFile, err := os.Open(REAPER_JSON_CONFIG)
if err == nil {
decoder := json.NewDecoder(configFile)
err = decoder.Decode(&config)
if err == nil {
useConfig = true
} else {
fmt.Printf(" - Error: Invalid json config: %s", err)
fmt.Printf(" - Using defaults ... ")
}
}

/* Start the grim reaper ... */
if useConfig {
go reaper.Start(config)

func main() {
sig := make(chan os.Signal, 1)
signal.Notify(sig, syscall.SIGUSR1)
/* Run the sleeper test setting the process attributes. */
go sleeper_test(true)

/* Start the grim reaper ... */
go reaper.Reap()
/* And run test without setting process attributes. */
go sleeper_test(false)

/* Start the initial set of workers ... */
start_workers()
} else {
go reaper.Reap()
}

for {
select {
case <-sig:
fmt.Println(" - Got SIGUSR1, starting more workers ...")
/* Start the initial set of workers ... */
start_workers()
}

} /* End of while doomsday ... */
for {
select {
case <-sig:
fmt.Println(" - Got SIGUSR1, adding workers ...")
start_workers()
}

} /* End of while doomsday ... */

} /* End of function main. */
} /* End of function main. */

0 comments on commit e2dea75

Please sign in to comment.