diff --git a/test/docker/docker_api_test.go b/test/docker/docker_api_test.go index 22e54dd..297e1e6 100644 --- a/test/docker/docker_api_test.go +++ b/test/docker/docker_api_test.go @@ -19,6 +19,7 @@ import ( "context" "strings" "testing" + "time" "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/network" @@ -94,7 +95,7 @@ func utilTestNoQueueManagerName(t *testing.T, hostName string, expectedName stri id := runContainer(t, cli, &containerConfig) defer cleanContainer(t, cli, id) waitForReady(t, cli, id) - _, out := execContainer(t, cli, id, []string{"dspmq"}) + out := execContainerWithOutput(t, cli, id, "mqm", []string{"dspmq"}) if !strings.Contains(out, search) { t.Errorf("Expected result of running dspmq to contain name=%v, got name=%v", search, out) } @@ -219,3 +220,46 @@ func TestStartQueueManagerFail(t *testing.T) { t.Errorf("Expected rc=1, got rc=%v", rc) } } + +func TestVolumeUnmount(t *testing.T) { + cli, err := client.NewEnvClient() + if err != nil { + t.Fatal(err) + } + vol := createVolume(t, cli) + defer removeVolume(t, cli, vol.Name) + containerConfig := container.Config{ + Image: imageName(), + Env: []string{"LICENSE=accept", "MQ_QMGR_NAME=qm1"}, + } + hostConfig := container.HostConfig{ + // SYS_ADMIN capability is required to unmount file systems + CapAdd: []string{ + "SYS_ADMIN", + }, + Binds: []string{ + coverageBind(t), + vol.Name + ":/mnt/mqm", + }, + } + networkingConfig := network.NetworkingConfig{} + ctr, err := cli.ContainerCreate(context.Background(), &containerConfig, &hostConfig, &networkingConfig, t.Name()) + if err != nil { + t.Fatal(err) + } + startContainer(t, cli, ctr.ID) + defer cleanContainer(t, cli, ctr.ID) + waitForReady(t, cli, ctr.ID) + // Unmount the volume as root + rc := execContainerWithExitCode(t, cli, ctr.ID, "root", []string{"umount", "-l", "-f", "/mnt/mqm"}) + if rc != 0 { + t.Fatalf("Expected umount to work with rc=0, got %v", rc) + } + time.Sleep(3 * time.Second) + rc = execContainerWithExitCode(t, cli, ctr.ID, "mqm", []string{"chkmqhealthy"}) + if rc == 0 { + t.Errorf("Expected chkmqhealthy to fail") + t.Logf(execContainerWithOutput(t, cli, ctr.ID, "mqm", []string{"df"})) + t.Logf(execContainerWithOutput(t, cli, ctr.ID, "mqm", []string{"ps", "-ef"})) + } +} diff --git a/test/docker/docker_api_test_util.go b/test/docker/docker_api_test_util.go index f9100ca..d1a30da 100644 --- a/test/docker/docker_api_test_util.go +++ b/test/docker/docker_api_test_util.go @@ -179,11 +179,45 @@ func waitForContainer(t *testing.T, cli *client.Client, ID string, timeout int64 return rc } -// execContainer runs the specified command inside the container, returning the -// exit code and the stdout/stderr string. -func execContainer(t *testing.T, cli *client.Client, ID string, cmd []string) (int, string) { +// execContainerWithExitCode runs a command in a running container, and returns the exit code +// Note: due to a bug in Docker/Moby code, you always get an exit code of 0 if you attach to the +// container to get output. This is why these are two separate commands. +func execContainerWithExitCode(t *testing.T, cli *client.Client, ID string, user string, cmd []string) int { config := types.ExecConfig{ - User: "mqm", + User: user, + Privileged: false, + Tty: false, + AttachStdin: false, + // Note that you still need to attach stdout/stderr, even though they're not wanted + AttachStdout: true, + AttachStderr: true, + Detach: false, + Cmd: cmd, + } + resp, err := cli.ContainerExecCreate(context.Background(), ID, config) + if err != nil { + t.Fatal(err) + } + cli.ContainerExecStart(context.Background(), resp.ID, types.ExecStartCheck{ + Detach: false, + Tty: false, + }) + if err != nil { + t.Fatal(err) + } + inspect, err := cli.ContainerExecInspect(context.Background(), resp.ID) + if err != nil { + t.Fatal(err) + } + return inspect.ExitCode +} + +// execContainerWithOutput runs a command in a running container, and returns the output from stdout/stderr +// Note: due to a bug in Docker/Moby code, you always get an exit code of 0 if you attach to the +// container to get output. This is why these are two separate commands. +func execContainerWithOutput(t *testing.T, cli *client.Client, ID string, user string, cmd []string) string { + config := types.ExecConfig{ + User: user, Privileged: false, Tty: false, AttachStdin: false, @@ -207,46 +241,19 @@ func execContainer(t *testing.T, cli *client.Client, ID string, cmd []string) (i if err != nil { t.Fatal(err) } - inspect, err := cli.ContainerExecInspect(context.Background(), resp.ID) - if err != nil { - t.Fatal(err) - } // TODO: For some reason, each line seems to start with an extra, random character buf, err := ioutil.ReadAll(hijack.Reader) if err != nil { t.Fatal(err) } hijack.Close() - return inspect.ExitCode, string(buf) + return string(buf) } func waitForReady(t *testing.T, cli *client.Client, ID string) { for { - resp, err := cli.ContainerExecCreate(context.Background(), ID, types.ExecConfig{ - User: "mqm", - Privileged: false, - Tty: false, - AttachStdin: false, - AttachStdout: true, - AttachStderr: true, - Detach: false, - Cmd: []string{"chkmqready"}, - }) - if err != nil { - t.Fatal(err) - } - cli.ContainerExecStart(context.Background(), resp.ID, types.ExecStartCheck{ - Detach: false, - Tty: false, - }) - if err != nil { - t.Fatal(err) - } - inspect, err := cli.ContainerExecInspect(context.Background(), resp.ID) - if err != nil { - t.Fatal(err) - } - if inspect.ExitCode == 0 { + rc := execContainerWithExitCode(t, cli, ID, "mqm", []string{"chkmqready"}) + if rc == 0 { t.Log("MQ is ready") return }