commit
a02bfa75fa
|
@ -1,6 +1,9 @@
|
|||
runsvinit
|
||||
runsvinit-*-*
|
||||
examples/runsvinit-linux-amd64*
|
||||
zombietest/zombie
|
||||
zombietest/runsvinit
|
||||
*.uptodate
|
||||
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
machine:
|
||||
services:
|
||||
- docker
|
||||
|
||||
test:
|
||||
override:
|
||||
- go test -v
|
||||
- make CIRCLECI=true SUDO=sudo RM= -C zombietest
|
45
main.go
45
main.go
|
@ -1,6 +1,7 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
|
@ -13,6 +14,9 @@ import (
|
|||
const etcService = "/etc/service"
|
||||
|
||||
func main() {
|
||||
reap := flag.Bool("reap", true, "reap orphan children")
|
||||
flag.Parse()
|
||||
|
||||
log.SetFlags(0)
|
||||
|
||||
runsvdir, err := exec.LookPath("runsvdir")
|
||||
|
@ -35,7 +39,12 @@ func main() {
|
|||
log.Printf("warning: I'm not PID 1, I'm PID %d", pid)
|
||||
}
|
||||
|
||||
go reapAll()
|
||||
if *reap {
|
||||
log.Print("reaping zombies")
|
||||
go reapLoop()
|
||||
} else {
|
||||
log.Print("NOT reaping zombies")
|
||||
}
|
||||
|
||||
supervisor := cmd(runsvdir, etcService)
|
||||
if err := supervisor.Start(); err != nil {
|
||||
|
@ -53,31 +62,33 @@ func main() {
|
|||
}
|
||||
}
|
||||
|
||||
func reapAll() {
|
||||
// From https://github.com/ramr/go-reaper/blob/master/reaper.go
|
||||
func reapLoop() {
|
||||
c := make(chan os.Signal)
|
||||
signal.Notify(c, syscall.SIGCHLD)
|
||||
for range c {
|
||||
go reapOne()
|
||||
reapChildren()
|
||||
}
|
||||
}
|
||||
|
||||
// From https://github.com/ramr/go-reaper/blob/master/reaper.go
|
||||
func reapOne() {
|
||||
var (
|
||||
ws syscall.WaitStatus
|
||||
pid int
|
||||
err error
|
||||
)
|
||||
func reapChildren() {
|
||||
for {
|
||||
pid, err = syscall.Wait4(-1, &ws, 0, nil)
|
||||
if err != syscall.EINTR {
|
||||
break
|
||||
var (
|
||||
ws syscall.WaitStatus
|
||||
pid int
|
||||
err error
|
||||
)
|
||||
for {
|
||||
pid, err = syscall.Wait4(-1, &ws, 0, nil)
|
||||
if err != syscall.EINTR {
|
||||
break
|
||||
}
|
||||
}
|
||||
if err == syscall.ECHILD {
|
||||
return // done
|
||||
}
|
||||
log.Printf("reaped child process %d (%+v)", pid, ws)
|
||||
}
|
||||
if err == syscall.ECHILD {
|
||||
return
|
||||
}
|
||||
log.Printf("reaped child process %d (%+v)", pid, ws)
|
||||
}
|
||||
|
||||
type signaler interface {
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
FROM alpine:latest
|
||||
RUN echo "http://dl-4.alpinelinux.org/alpine/edge/testing" >>/etc/apk/repositories && \
|
||||
apk add --update runit && \
|
||||
rm -rf /var/cache/apk/*
|
||||
|
||||
COPY zombie /
|
||||
RUN mkdir -p /etc/service/zombie
|
||||
COPY run-zombie /etc/service/zombie/run
|
||||
|
||||
COPY /runsvinit /
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
GO?=go
|
||||
SUDO?=
|
||||
RM?=--rm
|
||||
|
||||
.PHONY: test clean
|
||||
|
||||
test: .test.uptodate
|
||||
./test.bash
|
||||
|
||||
.test.uptodate: runsvinit zombie run-zombie Dockerfile
|
||||
$(SUDO) docker build -t zombietest .
|
||||
touch $@
|
||||
|
||||
runsvinit: ../*.go
|
||||
env GOOS=linux GOARCH=amd64 $(GO) build -o $@ github.com/peterbourgon/runsvinit
|
||||
|
||||
zombie: .build.uptodate
|
||||
$(SUDO) docker run $(RM) -v $(shell pwd):/mount zombietest-build cc -Wall -Werror -o /mount/zombie /zombie.c
|
||||
|
||||
.build.uptodate: build/zombie.c build/Dockerfile
|
||||
$(SUDO) docker build -t zombietest-build build/
|
||||
touch $@
|
||||
|
||||
clean:
|
||||
rm -rf .test.uptodate .build.uptodate runsvinit zombie
|
||||
$(SUDO) docker stop zombietest zombietest-build >/dev/null 2>&1 || true
|
||||
$(SUDO) docker rm zombietest zombietest-build >/dev/null 2>&1 || true
|
|
@ -0,0 +1,24 @@
|
|||
# zombietest
|
||||
|
||||
This directory contains an integration test to prove runsvinit is actually
|
||||
reaping zombies. `make` builds and executes the test as follows:
|
||||
|
||||
1. We produce a linux/amd64 runsvinit binary by setting GOOS/GOARCH and
|
||||
invoking the Go compiler. Requires Go 1.5, or Go 1.4 built with the
|
||||
appropriate cross-compile options.
|
||||
|
||||
2. The build/zombie.c program spawns five zombies and exits. We compile it for
|
||||
linux/amd64 via a zombietest-build container. We do this so `make` works
|
||||
from a Mac. This requires a working Docker installation.
|
||||
|
||||
3. Once we have linux/amd64 runsvinit and zombie binaries, we produce a
|
||||
zombietest container via the Dockerfile. That container contains a single
|
||||
runit service, /etc/service/zombie, which supervises the zombie binary. We
|
||||
provide no default ENTRYPOINT, so we can supply it at runtime.
|
||||
|
||||
4. Once the zombietest container is built, we invoke the test.bash script.
|
||||
That launches a version of the container with runsvinit set to NOT reap
|
||||
zombies, and after 1 second, verifies that zombies exist. Then, it launches
|
||||
a version of the container with runsvinit set to reap zombies, and after 1
|
||||
second, verifies that no zombies exist.
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
FROM alpine:latest
|
||||
RUN apk add --update gcc musl-dev && rm -rf /var/cache/apk/*
|
||||
COPY zombie.c /
|
|
@ -0,0 +1,18 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int main() {
|
||||
pid_t pid;
|
||||
int i;
|
||||
for (i = 0; i<5; i++) {
|
||||
pid = fork();
|
||||
if (pid > 0) {
|
||||
printf("Zombie #%d born\n", i + 1);
|
||||
} else {
|
||||
printf("Brains...\n");
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
#!/bin/sh
|
||||
|
||||
exec /zombie
|
|
@ -0,0 +1,47 @@
|
|||
#!/bin/bash
|
||||
|
||||
function zombies() {
|
||||
if [ -z "$CIRCLECI" ]
|
||||
then
|
||||
docker exec $C ps -o pid,stat | grep Z | wc -l
|
||||
else
|
||||
# https://circleci.com/docs/docker#docker-exec
|
||||
sudo lxc-attach -n "$(docker inspect --format '{{.Id}}' $C)" -- sh -c "ps -o pid,stat | grep Z | wc -l"
|
||||
fi
|
||||
}
|
||||
|
||||
function stop_rm() {
|
||||
docker stop $1
|
||||
docker rm $1
|
||||
}
|
||||
|
||||
SLEEP=1
|
||||
RC=0
|
||||
|
||||
C=$(docker run -d zombietest /runsvinit -reap=false)
|
||||
sleep $SLEEP
|
||||
NOREAP=$(zombies)
|
||||
echo -n without reaping, we have $NOREAP zombies...
|
||||
if [ "$NOREAP" -le "0" ]
|
||||
then
|
||||
echo " FAIL"
|
||||
RC=1
|
||||
else
|
||||
echo " good"
|
||||
fi
|
||||
stop_rm $C
|
||||
|
||||
C=$(docker run -d zombietest /runsvinit)
|
||||
sleep $SLEEP
|
||||
YESREAP=$(zombies)
|
||||
echo -n with reaping, we have $YESREAP zombies...
|
||||
if [ "$YESREAP" -gt "0" ]
|
||||
then
|
||||
echo " FAIL"
|
||||
RC=1
|
||||
else
|
||||
echo " good"
|
||||
fi
|
||||
stop_rm $C
|
||||
|
||||
exit $RC
|
Loading…
Reference in New Issue