From 09cc18cd0c75446e0379f62a053261e8bfce3b33 Mon Sep 17 00:00:00 2001 From: Rob Parker Date: Thu, 12 Apr 2018 11:01:20 +0100 Subject: [PATCH] backport docker exec timeout fix and web console test fix --- test/docker/Gopkg.toml | 2 +- test/docker/devconfig_test.go | 30 +------------- test/docker/devconfig_test_util.go | 42 +++++++++++++++----- test/docker/docker_api_test.go | 40 +++++++++++-------- test/docker/docker_api_test_util.go | 61 +++++++++-------------------- 5 files changed, 77 insertions(+), 98 deletions(-) diff --git a/test/docker/Gopkg.toml b/test/docker/Gopkg.toml index d13cde4c..c29f50ed 100644 --- a/test/docker/Gopkg.toml +++ b/test/docker/Gopkg.toml @@ -14,7 +14,7 @@ [[constraint]] name = "github.com/docker/docker" - version = "^1.12" + version = "=v17.03.2-ce" [[constraint]] name = "github.com/docker/go-connections" diff --git a/test/docker/devconfig_test.go b/test/docker/devconfig_test.go index 4c8ddd49..770e21b6 100644 --- a/test/docker/devconfig_test.go +++ b/test/docker/devconfig_test.go @@ -18,11 +18,7 @@ limitations under the License. package main import ( - "crypto/tls" - "fmt" - "net/http" "testing" - "time" "github.com/docker/docker/api/types/container" "github.com/docker/docker/client" @@ -41,31 +37,7 @@ func TestDevGoldenPath(t *testing.T) { defer cleanContainer(t, cli, id) waitForReady(t, cli, id) - waitForWebReady(t, cli, id) - - timeout := time.Duration(30 * time.Second) - // Disable TLS verification (server uses a self-signed certificate by default, - // so verification isn't useful anyway) - tr := &http.Transport{ - TLSClientConfig: &tls.Config{ - InsecureSkipVerify: true, - }, - } - httpClient := http.Client{ - Timeout: timeout, - Transport: tr, - } - - url := fmt.Sprintf("https://localhost:%s/ibmmq/rest/v1/admin/installation", getWebPort(t, cli, id)) - req, err := http.NewRequest("GET", url, nil) - req.SetBasicAuth("admin", "passw0rd") - resp, err := httpClient.Do(req) - if err != nil { - t.Fatal(err) - } - if resp.StatusCode != http.StatusOK { - t.Errorf("Expected HTTP status code %v from 'GET installation'; got %v", http.StatusOK, resp.StatusCode) - } + waitForWebReady(t, cli, id, insecureTLSConfig) // Stop the container cleanly stopContainer(t, cli, id) diff --git a/test/docker/devconfig_test_util.go b/test/docker/devconfig_test_util.go index e3051cbf..6ceb0622 100644 --- a/test/docker/devconfig_test_util.go +++ b/test/docker/devconfig_test_util.go @@ -18,25 +18,49 @@ limitations under the License. package main import ( - "crypto/tls" "fmt" "testing" "time" + "net/http" + "crypto/tls" "github.com/docker/docker/client" ) -func waitForWebReady(t *testing.T, cli *client.Client, ID string) { - config := tls.Config{InsecureSkipVerify: true} - a := fmt.Sprintf("localhost:%s", getWebPort(t, cli, ID)) + +const devAdminPassword string = "passw0rd" +const devAppPassword string = "passw0rd" + +// Disable TLS verification (server uses a self-signed certificate by default, +// so verification isn't useful anyway) +var insecureTLSConfig *tls.Config = &tls.Config{ + InsecureSkipVerify: true, +} + +func waitForWebReady(t *testing.T, cli *client.Client, ID string, tlsConfig *tls.Config) { + httpClient := http.Client{ + Timeout: time.Duration(3 * time.Second), + Transport: &http.Transport{ + TLSClientConfig: tlsConfig, + }, + } + url := fmt.Sprintf("https://localhost:%s/ibmmq/rest/v1/admin/installation", getWebPort(t, cli, ID)) for { - conn, err := tls.Dial("tcp", a, &config) - if err == nil { - conn.Close() - // Extra sleep to allow web apps to start - time.Sleep(3 * time.Second) + req, err := http.NewRequest("GET", url, nil) + req.SetBasicAuth("admin", devAdminPassword) + resp, err := httpClient.Do(req) + if err == nil && resp.StatusCode == http.StatusOK { t.Log("MQ web server is ready") return } + // conn, err := tls.Dial("tcp", a, &config) + // if err == nil { + // conn.Close() + // // Extra sleep to allow web apps to start + // time.Sleep(5 * time.Second) + // t.Log("MQ web server is ready") + // return + // } + time.Sleep(1 * time.Second) } } diff --git a/test/docker/docker_api_test.go b/test/docker/docker_api_test.go index 08be3068..24cf3002 100644 --- a/test/docker/docker_api_test.go +++ b/test/docker/docker_api_test.go @@ -126,7 +126,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 := execContainerWithOutput(t, cli, id, "mqm", []string{"dspmq"}) + _, out := execContainer(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) } @@ -285,16 +285,18 @@ func TestVolumeUnmount(t *testing.T) { 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"}) + rc, _ := execContainer(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"}) + rc, _ = execContainer(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"})) + _, output := execContainer(t, cli, ctr.ID, "mqm", []string{"df"}) + t.Logf(output) + _, output = execContainer(t, cli, ctr.ID, "mqm", []string{"ps", "-ef"}) + t.Logf(output) } } @@ -318,12 +320,12 @@ func TestZombies(t *testing.T) { 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"}) + _, out := execContainer(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) - out = execContainerWithOutput(t, cli, id, "mqm", []string{"bash", "-c", "ps -lA | grep '^. Z'"}) + _, out = execContainer(t, cli, id, "mqm", []string{"bash", "-c", "ps -lA | grep '^. Z'"}) if out != "" { count := strings.Count(out, "\n") + 1 t.Errorf("Expected zombies=0, got %v", count) @@ -356,7 +358,7 @@ func TestMQSC(t *testing.T) { id := runContainer(t, cli, &containerConfig) defer cleanContainer(t, cli, id) waitForReady(t, cli, id) - rc := execContainerWithExitCode(t, cli, id, "mqm", []string{"bash", "-c", "echo 'DISPLAY QLOCAL(test)' | runmqsc"}) + rc, _ := execContainer(t, cli, id, "mqm", []string{"bash", "-c", "echo 'DISPLAY QLOCAL(test)' | runmqsc"}) if rc != 0 { t.Fatalf("Expected runmqsc to exit with rc=0, got %v", rc) } @@ -392,17 +394,19 @@ func TestReadiness(t *testing.T) { id := runContainer(t, cli, &containerConfig) defer cleanContainer(t, cli, id) queueCheckCommand := fmt.Sprintf("echo 'DISPLAY QLOCAL(test%v)' | runmqsc", numQueues) - t.Log(execContainerWithOutput(t, cli, id, "root", []string{"cat", "/etc/mqm/test.mqsc"})) + _, output := execContainer(t, cli, id, "root", []string{"cat", "/etc/mqm/test.mqsc"}) + t.Log(output) for { - readyRC := execContainerWithExitCode(t, cli, id, "mqm", []string{"chkmqready"}) - queueCheckRC := execContainerWithExitCode(t, cli, id, "mqm", []string{"bash", "-c", queueCheckCommand}) + readyRC, _ := execContainer(t, cli, id, "mqm", []string{"chkmqready"}) + queueCheckRC, _ := execContainer(t, cli, id, "mqm", []string{"bash", "-c", queueCheckCommand}) t.Logf("readyRC=%v,queueCheckRC=%v\n", readyRC, queueCheckRC) if readyRC == 0 { if queueCheckRC != 0 { t.Fatalf("chkmqready returned %v when MQSC had not finished", readyRC) } else { // chkmqready says OK, and the last queue exists, so return - t.Log(execContainerWithOutput(t, cli, id, "root", []string{"bash", "-c", "echo 'DISPLAY QLOCAL(test1)' | runmqsc"})) + _, output = execContainer(t, cli, id, "root", []string{"bash", "-c", "echo 'DISPLAY QLOCAL(test1)' | runmqsc"}) + t.Log(output) return } } @@ -463,11 +467,13 @@ func TestErrorLogRotation(t *testing.T) { waitForReady(t, cli, id) dir := "/var/mqm/qmgrs/" + qmName + "/errors" // Generate some content for the error logs, by trying to put messages under an unauthorized user - // execContainerWithOutput(t, cli, id, "fred", []string{"bash", "-c", "for i in {1..30} ; do /opt/mqm/samp/bin/amqsput FAKE; done"}) - execContainerWithOutput(t, cli, id, "root", []string{"useradd", "fred"}) + // execContainer(t, cli, id, "fred", []string{"bash", "-c", "for i in {1..30} ; do /opt/mqm/samp/bin/amqsput FAKE; done"}) + execContainer(t, cli, id, "root", []string{"useradd", "fred"}) for { - execContainerWithOutput(t, cli, id, "fred", []string{"bash", "-c", "/opt/mqm/samp/bin/amqsput FAKE"}) - amqerr02size, err := strconv.Atoi(execContainerWithOutput(t, cli, id, "mqm", []string{"bash", "-c", "wc -c < " + filepath.Join(dir, "AMQERR02.json")})) + execContainer(t, cli, id, "fred", []string{"bash", "-c", "/opt/mqm/samp/bin/amqsput FAKE"}) + + _, atoiStr := execContainer(t, cli, id, "mqm", []string{"bash", "-c", "wc -c < " + filepath.Join(dir, "AMQERR02.json")}) + amqerr02size, err := strconv.Atoi(atoiStr) if err != nil { t.Fatal(err) } @@ -476,7 +482,7 @@ func TestErrorLogRotation(t *testing.T) { break } } - out := execContainerWithOutput(t, cli, id, "root", []string{"ls", "-l", dir}) + _, out := execContainer(t, cli, id, "root", []string{"ls", "-l", dir}) t.Log(out) stopContainer(t, cli, id) b := copyFromContainer(t, cli, id, filepath.Join(dir, "AMQERR01.json")) diff --git a/test/docker/docker_api_test_util.go b/test/docker/docker_api_test_util.go index 5409b456..ce9dd7f8 100644 --- a/test/docker/docker_api_test_util.go +++ b/test/docker/docker_api_test_util.go @@ -31,6 +31,7 @@ import ( "strings" "testing" "time" + "regexp" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" @@ -254,10 +255,9 @@ func waitForContainer(t *testing.T, cli *client.Client, ID string, timeout int64 return rc } -// 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 { +// execContainer runs a command in a running container, and returns the exit code and output +func execContainer(t *testing.T, cli *client.Client, ID string, user string, cmd []string) (int, string) { + rerun: config := types.ExecConfig{ User: user, Privileged: false, @@ -273,56 +273,23 @@ func execContainerWithExitCode(t *testing.T, cli *client.Client, ID string, user 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, - AttachStdout: true, - AttachStderr: true, - Detach: false, - Cmd: cmd, - } - resp, err := cli.ContainerExecCreate(context.Background(), ID, config) - if err != nil { - t.Fatal(err) - } hijack, err := cli.ContainerExecAttach(context.Background(), resp.ID, config) if err != nil { t.Fatal(err) } - err = cli.ContainerExecStart(context.Background(), resp.ID, types.ExecStartCheck{ + cli.ContainerExecStart(context.Background(), resp.ID, types.ExecStartCheck{ Detach: false, Tty: false, }) - if err != nil { - t.Fatal(err) - } // Wait for the command to finish + var exitcode int for { inspect, err := cli.ContainerExecInspect(context.Background(), resp.ID) if err != nil { t.Fatal(err) } if !inspect.Running { + exitcode = inspect.ExitCode break } } @@ -332,12 +299,22 @@ func execContainerWithOutput(t *testing.T, cli *client.Client, ID string, user s if err != nil { log.Fatal(err) } - return strings.TrimSpace(buf.String()) + + outputStr := strings.TrimSpace(buf.String()) + + // Before we go let's just double check it did actually run because sometimes we get a "Exec command already running error" + alreadyRunningErr := regexp.MustCompile("Error: Exec command .* is already running") + if alreadyRunningErr.MatchString(outputStr) { + time.Sleep(1 * time.Second) + goto rerun + } + + return exitcode, outputStr } func waitForReady(t *testing.T, cli *client.Client, ID string) { for { - rc := execContainerWithExitCode(t, cli, ID, "mqm", []string{"chkmqready"}) + rc, _ := execContainer(t, cli, ID, "mqm", []string{"chkmqready"}) if rc == 0 { t.Log("MQ is ready") return