Make metrics tests use a host port (#94)

* Make metrics tests use a host port

* fix port forwarding in devconfig tests
This commit is contained in:
Arthur Barr
2018-05-30 15:38:19 +01:00
committed by Rob Parker
parent 987b629a27
commit 6996f2b9b3
4 changed files with 96 additions and 200 deletions

View File

@@ -43,7 +43,7 @@ func TestDevGoldenPath(t *testing.T) {
"MQ_QMGR_NAME=qm1", "MQ_QMGR_NAME=qm1",
}, },
} }
id := runContainer(t, cli, &containerConfig) id := runContainerWithPorts(t, cli, &containerConfig, []int{9443})
defer cleanContainer(t, cli, id) defer cleanContainer(t, cli, id)
waitForReady(t, cli, id) waitForReady(t, cli, id)
waitForWebReady(t, cli, id, insecureTLSConfig) waitForWebReady(t, cli, id, insecureTLSConfig)
@@ -148,7 +148,7 @@ func TestDevConfigDisabled(t *testing.T) {
"MQ_DEV=false", "MQ_DEV=false",
}, },
} }
id := runContainer(t, cli, &containerConfig) id := runContainerWithPorts(t, cli, &containerConfig, []int{9443})
defer cleanContainer(t, cli, id) defer cleanContainer(t, cli, id)
waitForReady(t, cli, id) waitForReady(t, cli, id)
waitForWebReady(t, cli, id, insecureTLSConfig) waitForWebReady(t, cli, id, insecureTLSConfig)

View File

@@ -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 // runContainerWithPorts creates and starts a container, exposing the specified ports on the host.
// the container config, then the image name is retrieved from the TEST_IMAGE // If no image is specified in the container config, then the image name is retrieved from the TEST_IMAGE
// environment variable. // 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 == "" { if containerConfig.Image == "" {
containerConfig.Image = imageName() containerConfig.Image = imageName()
} }
@@ -228,15 +228,15 @@ func runContainer(t *testing.T, cli *client.Client, containerConfig *container.C
coverageBind(t), coverageBind(t),
terminationBind(t), terminationBind(t),
}, },
// Assign a random port for the web server on the host PortBindings: nat.PortMap{},
// TODO: Don't do this for all tests }
PortBindings: nat.PortMap{ for _, p := range ports {
"9443/tcp": []nat.PortBinding{ port := nat.Port(fmt.Sprintf("%v/tcp", p))
hostConfig.PortBindings[port] = []nat.PortBinding{
{ {
HostIP: "0.0.0.0", HostIP: "0.0.0.0",
}, },
}, }
},
} }
networkingConfig := network.NetworkingConfig{} networkingConfig := network.NetworkingConfig{}
t.Logf("Running container (%s)", containerConfig.Image) 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 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) { func runContainerOneShot(t *testing.T, cli *client.Client, command ...string) (int64, string) {
containerConfig := container.Config{ containerConfig := container.Config{
Entrypoint: command, Entrypoint: command,

View File

@@ -20,7 +20,6 @@ import (
"testing" "testing"
"time" "time"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/client" "github.com/docker/docker/client"
) )
@@ -30,85 +29,40 @@ func TestGoldenPathMetric(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
id := runContainerWithPorts(t, cli, metricsContainerConfig(), []int{defaultMetricPort})
containerConfig := container.Config{
Env: []string{
"LICENSE=accept",
"MQ_QMGR_NAME=qm1",
"MQ_ENABLE_METRICS=true",
},
}
id := runContainer(t, cli, &containerConfig)
defer cleanContainer(t, cli, id) defer cleanContainer(t, cli, id)
// hostname := getIPAddress(t, cli, id)
hostname := getIPAddress(t, cli, id) port := getMetricPort(t, cli, id)
port := defaultMetricPort
// Now the container is ready we prod the prometheus endpoint until it's up. // 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 // Call once as mq_prometheus 'ignores' the first call and will not return any metrics
_, err = getMetricsFromEndpoint(hostname, port) getMetrics(t, port)
if err != nil {
t.Logf("Failed to call metric endpoint - %v", err)
t.FailNow()
}
time.Sleep(15 * time.Second) time.Sleep(15 * time.Second)
metrics, err := getMetricsFromEndpoint(hostname, port) metrics := getMetrics(t, port)
if err != nil {
t.Logf("Failed to call metric endpoint - %v", err)
t.FailNow()
}
if len(metrics) <= 0 { 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() t.Fail()
} }
// Stop the container cleanly // Stop the container cleanly
stopContainer(t, cli, id) stopContainer(t, cli, id)
} }
func TestMetricNames(t *testing.T) { func TestMetricNames(t *testing.T) {
t.Parallel() t.Parallel()
approvedSuffixes := []string{"bytes", "seconds", "percentage", "count", "total"} approvedSuffixes := []string{"bytes", "seconds", "percentage", "count", "total"}
cli, err := client.NewEnvClient() cli, err := client.NewEnvClient()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
id := runContainerWithPorts(t, cli, metricsContainerConfig(), []int{defaultMetricPort})
containerConfig := container.Config{
Env: []string{
"LICENSE=accept",
"MQ_QMGR_NAME=qm1",
"MQ_ENABLE_METRICS=true",
},
}
id := runContainer(t, cli, &containerConfig)
defer cleanContainer(t, cli, id) defer cleanContainer(t, cli, id)
port := getMetricPort(t, cli, id)
hostname := getIPAddress(t, cli, id)
port := defaultMetricPort
// Now the container is ready we prod the prometheus endpoint until it's up. // 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 // Call once as mq_prometheus 'ignores' the first call
_, err = getMetricsFromEndpoint(hostname, port) getMetrics(t, port)
if err != nil {
t.Logf("Failed to call metric endpoint - %v", err)
t.FailNow()
}
time.Sleep(15 * time.Second) time.Sleep(15 * time.Second)
metrics, err := getMetricsFromEndpoint(hostname, port) metrics := getMetrics(t, port)
if err != nil {
t.Logf("Failed to call metric endpoint - %v", err)
t.FailNow()
}
if len(metrics) <= 0 { 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() t.Fail()
@@ -141,46 +95,22 @@ func TestMetricNames(t *testing.T) {
func TestMetricLabels(t *testing.T) { func TestMetricLabels(t *testing.T) {
t.Parallel() t.Parallel()
requiredLabels := []string{"qmgr"} requiredLabels := []string{"qmgr"}
cli, err := client.NewEnvClient() cli, err := client.NewEnvClient()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
id := runContainerWithPorts(t, cli, metricsContainerConfig(), []int{defaultMetricPort})
containerConfig := container.Config{
Env: []string{
"LICENSE=accept",
"MQ_QMGR_NAME=qm1",
"MQ_ENABLE_METRICS=true",
},
}
id := runContainer(t, cli, &containerConfig)
defer cleanContainer(t, cli, id) defer cleanContainer(t, cli, id)
port := getMetricPort(t, cli, id)
hostname := getIPAddress(t, cli, id)
port := defaultMetricPort
// Now the container is ready we prod the prometheus endpoint until it's up. // 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 // Call once as mq_prometheus 'ignores' the first call
_, err = getMetricsFromEndpoint(hostname, port) getMetrics(t, port)
if err != nil {
t.Logf("Failed to call metric endpoint - %v", err)
t.FailNow()
}
time.Sleep(15 * time.Second) time.Sleep(15 * time.Second)
metrics, err := getMetricsFromEndpoint(hostname, port) metrics := getMetrics(t, port)
if err != nil {
t.Logf("Failed to call metric endpoint - %v", err)
t.FailNow()
}
if len(metrics) <= 0 { if len(metrics) <= 0 {
t.Log("Expected some metrics to be returned but had none...") t.Error("Expected some metrics to be returned but had none")
t.Fail()
} }
for _, metric := range metrics { for _, metric := range metrics {
@@ -198,118 +128,63 @@ func TestMetricLabels(t *testing.T) {
} }
if !found { if !found {
t.Logf("Metric '%s' with labels %s does not have one or more required labels - %s", metric.Key, metric.Labels, requiredLabels) t.Errorf("Metric '%s' with labels %s does not have one or more required labels - %s", metric.Key, metric.Labels, requiredLabels)
t.Fail()
} }
} }
// Stop the container cleanly // Stop the container cleanly
stopContainer(t, cli, id) stopContainer(t, cli, id)
} }
func TestRapidFirePrometheus(t *testing.T) { func TestRapidFirePrometheus(t *testing.T) {
t.Parallel() t.Parallel()
cli, err := client.NewEnvClient() cli, err := client.NewEnvClient()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
id := runContainerWithPorts(t, cli, metricsContainerConfig(), []int{defaultMetricPort})
containerConfig := container.Config{
Env: []string{
"LICENSE=accept",
"MQ_QMGR_NAME=qm1",
"MQ_ENABLE_METRICS=true",
},
}
id := runContainer(t, cli, &containerConfig)
defer cleanContainer(t, cli, id) defer cleanContainer(t, cli, id)
port := getMetricPort(t, cli, id)
hostname := getIPAddress(t, cli, id)
port := defaultMetricPort
// Now the container is ready we prod the prometheus endpoint until it's up. // 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 // Call once as mq_prometheus 'ignores' the first call and will not return any metrics
_, err = getMetricsFromEndpoint(hostname, port) getMetrics(t, port)
if err != nil {
t.Logf("Failed to call metric endpoint - %v", err)
t.FailNow()
}
// Rapid fire it then check we're still happy // Rapid fire it then check we're still happy
for i := 0; i < 30; i++ { for i := 0; i < 30; i++ {
_, err := getMetricsFromEndpoint(hostname, port) getMetrics(t, port)
if err != nil {
t.Logf("Failed to call metric endpoint - %v", err)
t.FailNow()
}
time.Sleep(1 * time.Second) time.Sleep(1 * time.Second)
} }
time.Sleep(11 * time.Second) time.Sleep(11 * time.Second)
metrics := getMetrics(t, port)
metrics, err := getMetricsFromEndpoint(hostname, port)
if err != nil {
t.Logf("Failed to call metric endpoint - %v", err)
t.FailNow()
}
if len(metrics) <= 0 { if len(metrics) <= 0 {
t.Log("Expected some metrics to be returned but had none...") t.Error("Expected some metrics to be returned but had none")
t.Fail()
} }
// Stop the container cleanly // Stop the container cleanly
stopContainer(t, cli, id) stopContainer(t, cli, id)
} }
func TestSlowPrometheus(t *testing.T) { func TestSlowPrometheus(t *testing.T) {
t.Parallel() t.Parallel()
cli, err := client.NewEnvClient() cli, err := client.NewEnvClient()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
id := runContainerWithPorts(t, cli, metricsContainerConfig(), []int{defaultMetricPort})
containerConfig := container.Config{
Env: []string{
"LICENSE=accept",
"MQ_QMGR_NAME=qm1",
"MQ_ENABLE_METRICS=true",
},
}
id := runContainer(t, cli, &containerConfig)
defer cleanContainer(t, cli, id) defer cleanContainer(t, cli, id)
port := getMetricPort(t, cli, id)
hostname := getIPAddress(t, cli, id)
port := defaultMetricPort
// Now the container is ready we prod the prometheus endpoint until it's up. // 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 // Call once as mq_prometheus 'ignores' the first call and will not return any metrics
_, err = getMetricsFromEndpoint(hostname, port) getMetrics(t, port)
if err != nil {
t.Logf("Failed to call metric endpoint - %v", err)
t.FailNow()
}
// Send a request twice over a long period and check we're still happy // Send a request twice over a long period and check we're still happy
for i := 0; i < 2; i++ { for i := 0; i < 2; i++ {
time.Sleep(30 * time.Second) time.Sleep(30 * time.Second)
metrics, err := getMetricsFromEndpoint(hostname, port) metrics := getMetrics(t, port)
if err != nil {
t.Logf("Failed to call metric endpoint - %v", err)
t.FailNow()
}
if len(metrics) <= 0 { 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() t.Fail()
} }
} }
// Stop the container cleanly // Stop the container cleanly
stopContainer(t, cli, id) stopContainer(t, cli, id)
} }

View File

@@ -17,11 +17,18 @@ package main
import ( import (
"bufio" "bufio"
"context"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"strconv"
"strings" "strings"
"testing"
"time" "time"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/client"
"github.com/docker/go-connections/nat"
) )
type mqmetric struct { type mqmetric struct {
@@ -34,36 +41,26 @@ const defaultMetricURL = "/metrics"
const defaultMetricPort = 9157 const defaultMetricPort = 9157
const defaultMQNamespace = "ibmmq" const defaultMQNamespace = "ibmmq"
func getMetricsFromEndpoint(host string, port int) ([]mqmetric, error) { func getMetrics(t *testing.T, port int) []mqmetric {
returned := []mqmetric{} returned := []mqmetric{}
if host == "" { urlToUse := fmt.Sprintf("http://localhost:%v%v", port, defaultMetricURL)
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)
resp, err := http.Get(urlToUse) resp, err := http.Get(urlToUse)
if err != nil { if err != nil {
return returned, err t.Fatalf("Error from HTTP GET for metrics: %v", err)
return returned
} }
defer resp.Body.Close() defer resp.Body.Close()
metricsRaw, err := ioutil.ReadAll(resp.Body) metricsRaw, err := ioutil.ReadAll(resp.Body)
if err != nil { if err != nil {
return returned, err t.Fatalf("Error reading metrics data: %v", err)
return returned
} }
return convertRawMetricToMap(t, string(metricsRaw))
return convertRawMetricToMap(string(metricsRaw))
} }
// Also filters out all non "ibmmq" metrics // Also filters out all non "ibmmq" metrics
func convertRawMetricToMap(input string) ([]mqmetric, error) { func convertRawMetricToMap(t *testing.T, input string) []mqmetric {
returnList := []mqmetric{} returnList := []mqmetric{}
if input == "" {
return returnList, fmt.Errorf("Test Error - Raw metric output was nil")
}
scanner := bufio.NewScanner(strings.NewReader(input)) scanner := bufio.NewScanner(strings.NewReader(input))
for scanner.Scan() { for scanner.Scan() {
line := scanner.Text() line := scanner.Text()
@@ -78,7 +75,7 @@ func convertRawMetricToMap(input string) ([]mqmetric, error) {
//It's an IBM MQ metric! //It's an IBM MQ metric!
key, value, labelMap, err := convertMetricLineToMetric(line) key, value, labelMap, err := convertMetricLineToMetric(line)
if err != nil { 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{ toAdd := mqmetric{
@@ -90,7 +87,7 @@ func convertRawMetricToMap(input string) ([]mqmetric, error) {
returnList = append(returnList, toAdd) returnList = append(returnList, toAdd)
} }
return returnList, nil return returnList
} }
func convertMetricLineToMetric(input string) (string, string, map[string]string, error) { 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 return key, value, labelMap, nil
} }
func waitForMetricReady(host string, port int) error { func waitForMetricReady(t *testing.T, port int) {
if host == "" {
return fmt.Errorf("Test Error - Host was nil")
}
if port <= 0 {
return fmt.Errorf("Test Error - port was not above 0")
}
timeout := 12 // 12 * 5 = 1 minute timeout := 12 // 12 * 5 = 1 minute
for i := 0; i < timeout; i++ { 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) resp, err := http.Get(urlToUse)
if err == nil { if err == nil {
resp.Body.Close() resp.Body.Close()
return nil return
} }
time.Sleep(time.Second * 5) time.Sleep(time.Second * 5)
} }
t.Fatalf("Metric endpoint failed to startup in timely manner")
return fmt.Errorf("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
}
func metricsContainerConfig() *container.Config {
return &container.Config{
Env: []string{
"LICENSE=accept",
"MQ_QMGR_NAME=qm1",
"MQ_ENABLE_METRICS=true",
},
}
} }