diff --git a/test/docker/devconfig_test.go b/test/docker/devconfig_test.go index 3c0aa593..2bcb2bfc 100644 --- a/test/docker/devconfig_test.go +++ b/test/docker/devconfig_test.go @@ -43,7 +43,7 @@ func TestDevGoldenPath(t *testing.T) { "MQ_QMGR_NAME=qm1", }, } - id := runContainer(t, cli, &containerConfig) + id := runContainerWithPorts(t, cli, &containerConfig, []int{9443}) defer cleanContainer(t, cli, id) waitForReady(t, cli, id) waitForWebReady(t, cli, id, insecureTLSConfig) @@ -148,7 +148,7 @@ func TestDevConfigDisabled(t *testing.T) { "MQ_DEV=false", }, } - id := runContainer(t, cli, &containerConfig) + id := runContainerWithPorts(t, cli, &containerConfig, []int{9443}) defer cleanContainer(t, cli, id) waitForReady(t, cli, id) waitForWebReady(t, cli, id, insecureTLSConfig) diff --git a/test/docker/docker_api_test_util.go b/test/docker/docker_api_test_util.go index b61b766f..58578ca3 100644 --- a/test/docker/docker_api_test_util.go +++ b/test/docker/docker_api_test_util.go @@ -213,10 +213,10 @@ func cleanContainer(t *testing.T, cli *client.Client, ID string) { } } -// runContainer creates and starts a container. If no image is specified in -// the container config, then the image name is retrieved from the TEST_IMAGE +// runContainerWithPorts creates and starts a container, exposing the specified ports on the host. +// If no image is specified in the container config, then the image name is retrieved from the TEST_IMAGE // environment variable. -func runContainer(t *testing.T, cli *client.Client, containerConfig *container.Config) string { +func runContainerWithPorts(t *testing.T, cli *client.Client, containerConfig *container.Config, ports []int) string { if containerConfig.Image == "" { containerConfig.Image = imageName() } @@ -228,15 +228,15 @@ func runContainer(t *testing.T, cli *client.Client, containerConfig *container.C coverageBind(t), terminationBind(t), }, - // Assign a random port for the web server on the host - // TODO: Don't do this for all tests - PortBindings: nat.PortMap{ - "9443/tcp": []nat.PortBinding{ - { - HostIP: "0.0.0.0", - }, + PortBindings: nat.PortMap{}, + } + for _, p := range ports { + port := nat.Port(fmt.Sprintf("%v/tcp", p)) + hostConfig.PortBindings[port] = []nat.PortBinding{ + { + HostIP: "0.0.0.0", }, - }, + } } networkingConfig := network.NetworkingConfig{} t.Logf("Running container (%s)", containerConfig.Image) @@ -248,6 +248,13 @@ func runContainer(t *testing.T, cli *client.Client, containerConfig *container.C return ctr.ID } +// runContainer creates and starts a container. If no image is specified in +// the container config, then the image name is retrieved from the TEST_IMAGE +// environment variable. +func runContainer(t *testing.T, cli *client.Client, containerConfig *container.Config) string { + return runContainerWithPorts(t, cli, containerConfig, nil) +} + func runContainerOneShot(t *testing.T, cli *client.Client, command ...string) (int64, string) { containerConfig := container.Config{ Entrypoint: command, diff --git a/test/docker/mqmetric_test.go b/test/docker/mqmetric_test.go index 19c7fd95..881e11d5 100644 --- a/test/docker/mqmetric_test.go +++ b/test/docker/mqmetric_test.go @@ -20,7 +20,6 @@ import ( "testing" "time" - "github.com/docker/docker/api/types/container" "github.com/docker/docker/client" ) @@ -30,85 +29,40 @@ func TestGoldenPathMetric(t *testing.T) { if err != nil { t.Fatal(err) } - - containerConfig := container.Config{ - Env: []string{ - "LICENSE=accept", - "MQ_QMGR_NAME=qm1", - "MQ_ENABLE_METRICS=true", - }, - } - id := runContainer(t, cli, &containerConfig) + id := runContainerWithPorts(t, cli, metricsContainerConfig(), []int{defaultMetricPort}) defer cleanContainer(t, cli, id) - - hostname := getIPAddress(t, cli, id) - port := defaultMetricPort - + // hostname := getIPAddress(t, cli, id) + port := getMetricPort(t, cli, id) // Now the container is ready we prod the prometheus endpoint until it's up. - waitForMetricReady(hostname, port) - + waitForMetricReady(t, port) // Call once as mq_prometheus 'ignores' the first call and will not return any metrics - _, err = getMetricsFromEndpoint(hostname, port) - if err != nil { - t.Logf("Failed to call metric endpoint - %v", err) - t.FailNow() - } - + getMetrics(t, port) time.Sleep(15 * time.Second) - metrics, err := getMetricsFromEndpoint(hostname, port) - if err != nil { - t.Logf("Failed to call metric endpoint - %v", err) - t.FailNow() - } - + metrics := getMetrics(t, port) if len(metrics) <= 0 { t.Log("Expected some metrics to be returned but had none...") t.Fail() } - // Stop the container cleanly stopContainer(t, cli, id) } func TestMetricNames(t *testing.T) { t.Parallel() - approvedSuffixes := []string{"bytes", "seconds", "percentage", "count", "total"} cli, err := client.NewEnvClient() if err != nil { t.Fatal(err) } - - containerConfig := container.Config{ - Env: []string{ - "LICENSE=accept", - "MQ_QMGR_NAME=qm1", - "MQ_ENABLE_METRICS=true", - }, - } - id := runContainer(t, cli, &containerConfig) + id := runContainerWithPorts(t, cli, metricsContainerConfig(), []int{defaultMetricPort}) defer cleanContainer(t, cli, id) - - hostname := getIPAddress(t, cli, id) - port := defaultMetricPort - + port := getMetricPort(t, cli, id) // Now the container is ready we prod the prometheus endpoint until it's up. - waitForMetricReady(hostname, port) - + waitForMetricReady(t, port) // Call once as mq_prometheus 'ignores' the first call - _, err = getMetricsFromEndpoint(hostname, port) - if err != nil { - t.Logf("Failed to call metric endpoint - %v", err) - t.FailNow() - } - + getMetrics(t, port) time.Sleep(15 * time.Second) - metrics, err := getMetricsFromEndpoint(hostname, port) - if err != nil { - t.Logf("Failed to call metric endpoint - %v", err) - t.FailNow() - } - + metrics := getMetrics(t, port) if len(metrics) <= 0 { t.Log("Expected some metrics to be returned but had none...") t.Fail() @@ -141,46 +95,22 @@ func TestMetricNames(t *testing.T) { func TestMetricLabels(t *testing.T) { t.Parallel() - requiredLabels := []string{"qmgr"} cli, err := client.NewEnvClient() if err != nil { t.Fatal(err) } - - containerConfig := container.Config{ - Env: []string{ - "LICENSE=accept", - "MQ_QMGR_NAME=qm1", - "MQ_ENABLE_METRICS=true", - }, - } - id := runContainer(t, cli, &containerConfig) + id := runContainerWithPorts(t, cli, metricsContainerConfig(), []int{defaultMetricPort}) defer cleanContainer(t, cli, id) - - hostname := getIPAddress(t, cli, id) - port := defaultMetricPort - + port := getMetricPort(t, cli, id) // Now the container is ready we prod the prometheus endpoint until it's up. - waitForMetricReady(hostname, port) - + waitForMetricReady(t, port) // Call once as mq_prometheus 'ignores' the first call - _, err = getMetricsFromEndpoint(hostname, port) - if err != nil { - t.Logf("Failed to call metric endpoint - %v", err) - t.FailNow() - } - + getMetrics(t, port) time.Sleep(15 * time.Second) - metrics, err := getMetricsFromEndpoint(hostname, port) - if err != nil { - t.Logf("Failed to call metric endpoint - %v", err) - t.FailNow() - } - + metrics := getMetrics(t, port) if len(metrics) <= 0 { - t.Log("Expected some metrics to be returned but had none...") - t.Fail() + t.Error("Expected some metrics to be returned but had none") } for _, metric := range metrics { @@ -198,118 +128,63 @@ func TestMetricLabels(t *testing.T) { } if !found { - t.Logf("Metric '%s' with labels %s does not have one or more required labels - %s", metric.Key, metric.Labels, requiredLabels) - t.Fail() + t.Errorf("Metric '%s' with labels %s does not have one or more required labels - %s", metric.Key, metric.Labels, requiredLabels) } } - // Stop the container cleanly stopContainer(t, cli, id) } func TestRapidFirePrometheus(t *testing.T) { t.Parallel() - cli, err := client.NewEnvClient() if err != nil { t.Fatal(err) } - - containerConfig := container.Config{ - Env: []string{ - "LICENSE=accept", - "MQ_QMGR_NAME=qm1", - "MQ_ENABLE_METRICS=true", - }, - } - id := runContainer(t, cli, &containerConfig) + id := runContainerWithPorts(t, cli, metricsContainerConfig(), []int{defaultMetricPort}) defer cleanContainer(t, cli, id) - - hostname := getIPAddress(t, cli, id) - port := defaultMetricPort - + port := getMetricPort(t, cli, id) // Now the container is ready we prod the prometheus endpoint until it's up. - waitForMetricReady(hostname, port) - + waitForMetricReady(t, port) // Call once as mq_prometheus 'ignores' the first call and will not return any metrics - _, err = getMetricsFromEndpoint(hostname, port) - if err != nil { - t.Logf("Failed to call metric endpoint - %v", err) - t.FailNow() - } - + getMetrics(t, port) // Rapid fire it then check we're still happy for i := 0; i < 30; i++ { - _, err := getMetricsFromEndpoint(hostname, port) - if err != nil { - t.Logf("Failed to call metric endpoint - %v", err) - t.FailNow() - } + getMetrics(t, port) time.Sleep(1 * time.Second) } - time.Sleep(11 * time.Second) - - metrics, err := getMetricsFromEndpoint(hostname, port) - if err != nil { - t.Logf("Failed to call metric endpoint - %v", err) - t.FailNow() - } + metrics := getMetrics(t, port) if len(metrics) <= 0 { - t.Log("Expected some metrics to be returned but had none...") - t.Fail() + t.Error("Expected some metrics to be returned but had none") } - // Stop the container cleanly stopContainer(t, cli, id) } func TestSlowPrometheus(t *testing.T) { t.Parallel() - cli, err := client.NewEnvClient() if err != nil { t.Fatal(err) } - - containerConfig := container.Config{ - Env: []string{ - "LICENSE=accept", - "MQ_QMGR_NAME=qm1", - "MQ_ENABLE_METRICS=true", - }, - } - id := runContainer(t, cli, &containerConfig) + id := runContainerWithPorts(t, cli, metricsContainerConfig(), []int{defaultMetricPort}) defer cleanContainer(t, cli, id) - - hostname := getIPAddress(t, cli, id) - port := defaultMetricPort - + port := getMetricPort(t, cli, id) // Now the container is ready we prod the prometheus endpoint until it's up. - waitForMetricReady(hostname, port) - + waitForMetricReady(t, port) // Call once as mq_prometheus 'ignores' the first call and will not return any metrics - _, err = getMetricsFromEndpoint(hostname, port) - if err != nil { - t.Logf("Failed to call metric endpoint - %v", err) - t.FailNow() - } - + getMetrics(t, port) // Send a request twice over a long period and check we're still happy for i := 0; i < 2; i++ { time.Sleep(30 * time.Second) - metrics, err := getMetricsFromEndpoint(hostname, port) - if err != nil { - t.Logf("Failed to call metric endpoint - %v", err) - t.FailNow() - } + metrics := getMetrics(t, port) if len(metrics) <= 0 { - t.Log("Expected some metrics to be returned but had none...") + t.Log("Expected some metrics to be returned but had none") t.Fail() } } - // Stop the container cleanly stopContainer(t, cli, id) } diff --git a/test/docker/mqmetric_test_util.go b/test/docker/mqmetric_test_util.go index dfd4eb9b..b3541c90 100644 --- a/test/docker/mqmetric_test_util.go +++ b/test/docker/mqmetric_test_util.go @@ -17,11 +17,18 @@ package main import ( "bufio" + "context" "fmt" "io/ioutil" "net/http" + "strconv" "strings" + "testing" "time" + + "github.com/docker/docker/api/types/container" + "github.com/docker/docker/client" + "github.com/docker/go-connections/nat" ) type mqmetric struct { @@ -34,36 +41,26 @@ const defaultMetricURL = "/metrics" const defaultMetricPort = 9157 const defaultMQNamespace = "ibmmq" -func getMetricsFromEndpoint(host string, port int) ([]mqmetric, error) { +func getMetrics(t *testing.T, port int) []mqmetric { returned := []mqmetric{} - if host == "" { - return returned, fmt.Errorf("Test Error - Host was nil") - } - if port <= 0 { - return returned, fmt.Errorf("Test Error - port was not above 0") - } - urlToUse := fmt.Sprintf("http://%s:%d%s", host, port, defaultMetricURL) - + urlToUse := fmt.Sprintf("http://localhost:%v%v", port, defaultMetricURL) resp, err := http.Get(urlToUse) if err != nil { - return returned, err + t.Fatalf("Error from HTTP GET for metrics: %v", err) + return returned } defer resp.Body.Close() metricsRaw, err := ioutil.ReadAll(resp.Body) if err != nil { - return returned, err + t.Fatalf("Error reading metrics data: %v", err) + return returned } - - return convertRawMetricToMap(string(metricsRaw)) + return convertRawMetricToMap(t, string(metricsRaw)) } // Also filters out all non "ibmmq" metrics -func convertRawMetricToMap(input string) ([]mqmetric, error) { +func convertRawMetricToMap(t *testing.T, input string) []mqmetric { returnList := []mqmetric{} - if input == "" { - return returnList, fmt.Errorf("Test Error - Raw metric output was nil") - } - scanner := bufio.NewScanner(strings.NewReader(input)) for scanner.Scan() { line := scanner.Text() @@ -78,7 +75,7 @@ func convertRawMetricToMap(input string) ([]mqmetric, error) { //It's an IBM MQ metric! key, value, labelMap, err := convertMetricLineToMetric(line) if err != nil { - return returnList, fmt.Errorf("ibmmq_ metric could not be deciphered - %v", err) + t.Fatalf("ibmmq_ metric could not be deciphered - %v", err) } toAdd := mqmetric{ @@ -90,7 +87,7 @@ func convertRawMetricToMap(input string) ([]mqmetric, error) { returnList = append(returnList, toAdd) } - return returnList, nil + return returnList } func convertMetricLineToMetric(input string) (string, string, map[string]string, error) { @@ -137,24 +134,41 @@ func convertMetricLineToMetric(input string) (string, string, map[string]string, return key, value, labelMap, nil } -func waitForMetricReady(host string, port int) error { - if host == "" { - return fmt.Errorf("Test Error - Host was nil") - } - if port <= 0 { - return fmt.Errorf("Test Error - port was not above 0") - } +func waitForMetricReady(t *testing.T, port int) { timeout := 12 // 12 * 5 = 1 minute for i := 0; i < timeout; i++ { - urlToUse := fmt.Sprintf("http://%s:%d", host, port) + urlToUse := fmt.Sprintf("http://localhost:%v", port) resp, err := http.Get(urlToUse) if err == nil { resp.Body.Close() - return nil + return } time.Sleep(time.Second * 5) } + t.Fatalf("Metric endpoint failed to startup in timely manner") +} + +func getMetricPort(t *testing.T, cli *client.Client, ID string) int { + i, err := cli.ContainerInspect(context.Background(), ID) + if err != nil { + t.Fatal(err) + } + port := nat.Port(fmt.Sprintf("%v/tcp", defaultMetricPort)) + portString := i.NetworkSettings.Ports[port][0].HostPort + p, err := strconv.Atoi(portString) + if err != nil { + t.Fatal(err) + } + return p +} - return fmt.Errorf("Metric endpoint failed to startup in timely manner") +func metricsContainerConfig() *container.Config { + return &container.Config{ + Env: []string{ + "LICENSE=accept", + "MQ_QMGR_NAME=qm1", + "MQ_ENABLE_METRICS=true", + }, + } }