Add TestZombies and improve exec output handling

This commit is contained in:
Arthur Barr
2017-11-29 15:50:10 +00:00
parent 5b0259ec6e
commit a80b839c14
4 changed files with 70 additions and 7 deletions

View File

@@ -19,6 +19,7 @@ package main
import ( import (
"errors" "errors"
"fmt"
"io/ioutil" "io/ioutil"
"log" "log"
"os" "os"
@@ -30,6 +31,20 @@ import (
"github.com/ibm-messaging/mq-container/internal/name" "github.com/ibm-messaging/mq-container/internal/name"
) )
var debug = false
func logDebug(msg string) {
if debug {
log.Printf("DEBUG: %v", msg)
}
}
func logDebugf(format string, args ...interface{}) {
if debug {
log.Printf("DEBUG: %v", fmt.Sprintf(format, args...))
}
}
// createDirStructure creates the default MQ directory structure under /var/mqm // createDirStructure creates the default MQ directory structure under /var/mqm
func createDirStructure() error { func createDirStructure() error {
out, _, err := command.Run("/opt/mqm/bin/crtmqdir", "-f", "-s") out, _, err := command.Run("/opt/mqm/bin/crtmqdir", "-f", "-s")
@@ -127,6 +142,10 @@ func stopQueueManager(name string) error {
} }
func doMain() error { func doMain() error {
debugEnv, ok := os.LookupEnv("DEBUG")
if ok && (debugEnv == "true" || debugEnv == "1") {
debug = true
}
accepted, err := checkLicense() accepted, err := checkLicense()
if err != nil { if err != nil {
return err return err

View File

@@ -50,11 +50,13 @@ func signalHandler(qmgr string) chan int {
// End the goroutine // End the goroutine
return return
case <-reapSignals: case <-reapSignals:
logDebug("Received SIGCHLD signal")
reapZombies() reapZombies()
case job := <-control: case job := <-control:
switch { switch {
case job == startReaping: case job == startReaping:
// Add SIGCHLD to the list of signals we're listening to // Add SIGCHLD to the list of signals we're listening to
logDebug("Listening for SIGCHLD signals")
signal.Notify(reapSignals, syscall.SIGCHLD) signal.Notify(reapSignals, syscall.SIGCHLD)
case job == reapNow: case job == reapNow:
reapZombies() reapZombies()
@@ -75,5 +77,6 @@ func reapZombies() {
if pid == 0 || err == unix.ECHILD { if pid == 0 || err == unix.ECHILD {
return return
} }
logDebugf("Reaped PID %v", pid)
} }
} }

View File

@@ -17,6 +17,7 @@ package main
import ( import (
"context" "context"
"strconv"
"strings" "strings"
"testing" "testing"
"time" "time"
@@ -270,3 +271,38 @@ func TestVolumeUnmount(t *testing.T) {
t.Logf(execContainerWithOutput(t, cli, ctr.ID, "mqm", []string{"ps", "-ef"})) t.Logf(execContainerWithOutput(t, cli, ctr.ID, "mqm", []string{"ps", "-ef"}))
} }
} }
// TestZombies starts a queue manager, then causes a zombie process to be
// created, then checks that no zombies exist (runmqserver should reap them)
func TestZombies(t *testing.T) {
cli, err := client.NewEnvClient()
if err != nil {
t.Fatal(err)
}
containerConfig := container.Config{
Env: []string{"LICENSE=accept", "MQ_QMGR_NAME=qm1", "DEBUG=true"},
//ExposedPorts: ports,
ExposedPorts: nat.PortSet{
"1414/tcp": struct{}{},
},
}
id := runContainer(t, cli, &containerConfig)
defer cleanContainer(t, cli, id)
waitForReady(t, cli, id)
// Kill an MQ process with children. After it is killed, its children
// will be adopted by PID 1, and should then be reaped when they die.
out := execContainerWithOutput(t, cli, id, "mqm", []string{"pkill", "--signal", "kill", "-c", "amqzxma0"})
if out == "0" {
t.Fatalf("Expected pkill to kill a process, got %v", out)
}
time.Sleep(3 * time.Second)
// Create a zombie process for up to ten seconds
out = execContainerWithOutput(t, cli, id, "mqm", []string{"bash", "-c", "ps -lA | grep '^. Z' | wc -l"})
count, err := strconv.Atoi(out)
if err != nil {
t.Fatal(err)
}
if count != 0 {
t.Fatalf("Expected zombies=0, got %v", count)
}
}

View File

@@ -23,6 +23,7 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"strconv" "strconv"
"strings"
"testing" "testing"
"time" "time"
@@ -32,6 +33,7 @@ import (
"github.com/docker/docker/api/types/volume" "github.com/docker/docker/api/types/volume"
"github.com/docker/docker/client" "github.com/docker/docker/client"
"github.com/docker/go-connections/nat" "github.com/docker/go-connections/nat"
"github.com/moby/moby/pkg/stdcopy"
) )
func imageName() string { func imageName() string {
@@ -241,13 +243,13 @@ func execContainerWithOutput(t *testing.T, cli *client.Client, ID string, user s
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
// TODO: For some reason, each line seems to start with an extra, random character buf := new(bytes.Buffer)
buf, err := ioutil.ReadAll(hijack.Reader) // Each output line has a header, which needs to be removed
_, err = stdcopy.StdCopy(buf, buf, hijack.Reader)
if err != nil { if err != nil {
t.Fatal(err) log.Fatal(err)
} }
hijack.Close() return strings.TrimSpace(buf.String())
return string(buf)
} }
func waitForReady(t *testing.T, cli *client.Client, ID string) { func waitForReady(t *testing.T, cli *client.Client, ID string) {
@@ -320,8 +322,11 @@ func inspectLogs(t *testing.T, cli *client.Client, ID string) string {
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
buf.ReadFrom(reader) // Each output line has a header, which needs to be removed
_, err = stdcopy.StdCopy(buf, buf, reader)
if err != nil {
log.Fatal(err)
}
return buf.String() return buf.String()
} }