Merge pull request #31 from riccardobiraghi/master

Fix build and tests in Windows Subsystem for Linux
This commit is contained in:
Arthur Barr
2018-04-09 16:36:24 +01:00
committed by GitHub
6 changed files with 72 additions and 28 deletions

3
.gitattributes vendored Normal file
View File

@@ -0,0 +1,3 @@
# Fore LF line endings to prevent errors in Bash on Windows
* text=auto
*.sh text eol=lf

View File

@@ -54,6 +54,13 @@ MQ_IMAGE_DEVSERVER_BASE=mqadvanced-server-dev-base:$(MQ_VERSION)-$(ARCH)-$(BASE_
# Docker image name to use for JMS tests # Docker image name to use for JMS tests
DEV_JMS_IMAGE=mq-dev-jms-test DEV_JMS_IMAGE=mq-dev-jms-test
ifneq (,$(findstring Microsoft,$(shell uname -r)))
DOWNLOADS_DIR=$(patsubst /mnt/c%,C:%,$(realpath ./downloads/))
else
DOWNLOADS_DIR=$(realpath ./downloads/)
endif
# Try to figure out which archive to use from the BASE_IMAGE # Try to figure out which archive to use from the BASE_IMAGE
ifeq "$(findstring ubuntu,$(BASE_IMAGE))" "ubuntu" ifeq "$(findstring ubuntu,$(BASE_IMAGE))" "ubuntu"
MQ_ARCHIVE_TYPE=UBUNTU MQ_ARCHIVE_TYPE=UBUNTU
@@ -137,7 +144,7 @@ test-unit:
.PHONY: test-advancedserver .PHONY: test-advancedserver
test-advancedserver: test/docker/vendor test-advancedserver: test/docker/vendor
$(info $(SPACER)$(shell printf $(TITLE)"Test $(MQ_IMAGE_ADVANCEDSERVER) on Docker"$(END))) $(info $(SPACER)$(shell printf $(TITLE)"Test $(MQ_IMAGE_ADVANCEDSERVER) on Docker"$(END)))
cd test/docker && TEST_IMAGE=$(MQ_IMAGE_ADVANCEDSERVER) go test -parallel $(NUM_CPU) $(TEST_OPTS_DOCKER) cd test/docker && TEST_IMAGE=$(MQ_IMAGE_ADVANCEDSERVER) go test -timeout 20m -parallel $(NUM_CPU) $(TEST_OPTS_DOCKER)
.PHONY: build-devjmstest .PHONY: build-devjmstest
build-devjmstest: build-devjmstest:
@@ -181,7 +188,7 @@ define docker-build-mq
--name $(BUILD_SERVER_CONTAINER) \ --name $(BUILD_SERVER_CONTAINER) \
--network build \ --network build \
--network-alias build \ --network-alias build \
--volume "$(realpath ./downloads/)":/usr/share/nginx/html:ro \ --volume $(DOWNLOADS_DIR):/usr/share/nginx/html:ro \
--detach \ --detach \
nginx:alpine nginx:alpine
# Build the new image (use --pull to make sure we have the latest base image) # Build the new image (use --pull to make sure we have the latest base image)

View File

@@ -1,10 +1,12 @@
# Building a Docker image # Building a Docker image
## Prerequisites ## Prerequisites
You need to ensure you have the following tools installed: You need to ensure you have the following tools installed:
* [Docker](https://www.docker.com/) V17.06.1 or later * [Docker](https://www.docker.com/) V17.06.1 or later
* [GNU make](https://www.gnu.org/software/make/) * [GNU make](https://www.gnu.org/software/make/)
If you are working in the Windows Subsystem for Linux, follow [this guide by Microsoft to set up Docker](https://blogs.msdn.microsoft.com/commandline/2017/12/08/cross-post-wsl-interoperability-with-docker/) first.
## Building a production image ## Building a production image
This procedure works for building the MQ Continuous Delivery release, on `x86_64`, `ppc64le` and `s390x` architectures. This procedure works for building the MQ Continuous Delivery release, on `x86_64`, `ppc64le` and `s390x` architectures.
@@ -46,4 +48,3 @@ Note that if you are using Red Hat Enterprise Linux, you will need to create you
## Installed components ## Installed components
This image includes the core MQ server, Java, language packs, and GSKit. This can be configured by setting the `MQ_PACKAGES` argument to `make`, or directly as a [Docker build argument](https://docs.docker.com/engine/reference/commandline/build/#set-build-time-variables-build-arg). This image includes the core MQ server, Java, language packs, and GSKit. This can be configured by setting the `MQ_PACKAGES` argument to `make`, or directly as a [Docker build argument](https://docs.docker.com/engine/reference/commandline/build/#set-build-time-variables-build-arg).

View File

@@ -1,4 +1,4 @@
# Testing # Testing
## Prerequisites ## Prerequisites
You need to ensure you have the following tools installed: You need to ensure you have the following tools installed:
@@ -8,15 +8,6 @@ You need to ensure you have the following tools installed:
* [dep](https://github.com/golang/dep) (official Go dependency management tool) - needed to prepare for running the tests * [dep](https://github.com/golang/dep) (official Go dependency management tool) - needed to prepare for running the tests
* [Helm](https://helm.sh) - only needed for running the Kubernetes tests * [Helm](https://helm.sh) - only needed for running the Kubernetes tests
For running the Kubernetes tests, a Kubernetes environment is needed, for example [Minikube](https://github.com/kubernetes/minikube) or [IBM Cloud Private](https://www.ibm.com/cloud-computing/products/ibm-cloud-private/).
## Preparing to run the tests
The test dependencies are not included with the source code, so you need to download them before you can run them. This can be done with the following command, which uses the `dep` tool:
```
make deps
```
## Running the tests ## Running the tests
There are two main sets of tests: There are two main sets of tests:
@@ -57,7 +48,7 @@ make build-advancedserver-cover
make test-advancedserver-cover make test-advancedserver-cover
``` ```
In order to generate code coverage metrics from the Docker tests, the build step creates a new Docker image with an instrumented version of the code. Each test is then run individually, producing a coverage report each under `test/docker/coverage/`. These individual reports are then combined. The combined report is written to the `coverage` directory. In order to generate code coverage metrics from the Docker tests, the build step creates a new Docker image with an instrumented version of the code. Each test is then run individually, producing a coverage report each under `test/docker/coverage/`. These individual reports are then combined. The combined report is written to the `coverage` directory.
### Running the Kubernetes tests ### Running the Kubernetes tests

View File

@@ -24,6 +24,7 @@ import (
"fmt" "fmt"
"io" "io"
"path/filepath" "path/filepath"
"regexp"
"strconv" "strconv"
"strings" "strings"
"testing" "testing"
@@ -358,9 +359,10 @@ func TestMQSC(t *testing.T) {
id := runContainer(t, cli, &containerConfig) id := runContainer(t, cli, &containerConfig)
defer cleanContainer(t, cli, id) defer cleanContainer(t, cli, id)
waitForReady(t, cli, id) waitForReady(t, cli, id)
rc, _ := execContainer(t, cli, id, "mqm", []string{"bash", "-c", "echo 'DISPLAY QLOCAL(test)' | runmqsc"}) rc, mqscOutput := execContainer(t, cli, id, "mqm", []string{"bash", "-c", "echo 'DISPLAY QLOCAL(test)' | runmqsc"})
if rc != 0 { if rc != 0 {
t.Fatalf("Expected runmqsc to exit with rc=0, got %v", rc) r := regexp.MustCompile("AMQ[0-9][0-9][0-9][0-9]E")
t.Fatalf("Expected runmqsc to exit with rc=0, got %v with error %v", rc, r.FindString(mqscOutput))
} }
} }
@@ -398,11 +400,13 @@ func TestReadiness(t *testing.T) {
t.Log(mqsc) t.Log(mqsc)
for { for {
readyRC, _ := execContainer(t, cli, id, "mqm", []string{"chkmqready"}) readyRC, _ := execContainer(t, cli, id, "mqm", []string{"chkmqready"})
queueCheckRC, _ := execContainer(t, cli, id, "mqm", []string{"bash", "-c", queueCheckCommand}) queueCheckRC, queueCheckOut := execContainer(t, cli, id, "mqm", []string{"bash", "-c", queueCheckCommand})
t.Logf("readyRC=%v,queueCheckRC=%v\n", readyRC, queueCheckRC) t.Logf("readyRC=%v,queueCheckRC=%v\n", readyRC, queueCheckRC)
if readyRC == 0 { if readyRC == 0 {
if queueCheckRC != 0 { if (queueCheckRC != 0) {
t.Fatalf("chkmqready returned %v when MQSC had not finished", readyRC) r := regexp.MustCompile("AMQ[0-9][0-9][0-9][0-9]E")
t.Fatalf("Runmqsc returned %v with error %v. chkmqready returned %v when MQSC had not finished", queueCheckRC, r.FindString(queueCheckOut), readyRC)
} else { } else {
// chkmqready says OK, and the last queue exists, so return // chkmqready says OK, and the last queue exists, so return
_, runmqsc := execContainer(t, cli, id, "root", []string{"bash", "-c", "echo 'DISPLAY QLOCAL(test1)' | runmqsc"}) _, runmqsc := execContainer(t, cli, id, "root", []string{"bash", "-c", "echo 'DISPLAY QLOCAL(test1)' | runmqsc"})

View File

@@ -25,7 +25,9 @@ import (
"io" "io"
"io/ioutil" "io/ioutil"
"os" "os"
"os/exec"
"path/filepath" "path/filepath"
"runtime"
"strconv" "strconv"
"strings" "strings"
"testing" "testing"
@@ -80,17 +82,53 @@ func coverageBind(t *testing.T) string {
return coverageDir(t) + ":/var/coverage" return coverageDir(t) + ":/var/coverage"
} }
// terminationLog returns the name of the file to use for the termination log message // isWSL return whether we are running in the Windows Subsystem for Linux
func terminationLog(t *testing.T) string { func isWSL(t *testing.T) bool {
if runtime.GOOS == "linux" {
uname, err := exec.Command("uname", "-r").Output()
if (err != nil) {
t.Fatal(err)
}
return strings.Contains(string(uname), "Microsoft")
} else {
return false
}
}
// getTempDir get the path of the tmp directory, in UNIX or OS-specific style
func getTempDir(t *testing.T, unixStylePath bool) string {
if isWSL(t) {
if unixStylePath {
return "/mnt/c/Temp/"
} else {
return "C:/Temp/"
}
} else {
return "/tmp/"
}
}
// terminationLogUnixPath returns the name of the file to use for the termination log message, with a UNIX path
func terminationLogUnixPath(t *testing.T) string {
// Warning: this directory must be accessible to the Docker daemon, // Warning: this directory must be accessible to the Docker daemon,
// in order to enable the bind mount // in order to enable the bind mount
return "/tmp/" + t.Name() + "-termination-log" return getTempDir(t, true) + t.Name() + "-termination-log"
}
// terminationLogOSPath returns the name of the file to use for the termination log message, with an OS specific path
func terminationLogOSPath(t *testing.T) string {
// Warning: this directory must be accessible to the Docker daemon,
// in order to enable the bind mount
return getTempDir(t, false) + t.Name() + "-termination-log"
} }
// terminationBind returns a string to use to bind-mount a termination log file. // terminationBind returns a string to use to bind-mount a termination log file.
// This is done using a bind, because you can't copy files from /dev out of the container. // This is done using a bind, because you can't copy files from /dev out of the container.
func terminationBind(t *testing.T) string { func terminationBind(t *testing.T) string {
n := terminationLog(t) n := terminationLogUnixPath(t)
// Remove it if it already exists // Remove it if it already exists
os.Remove(n) os.Remove(n)
// Create the empty file // Create the empty file
@@ -99,12 +137,12 @@ func terminationBind(t *testing.T) string {
t.Fatal(err) t.Fatal(err)
} }
f.Close() f.Close()
return n + ":/dev/termination-log" return terminationLogOSPath(t) + ":/dev/termination-log"
} }
// Returns the termination message, or an empty string if not set // terminationMessage return the termination message, or an empty string if not set
func terminationMessage(t *testing.T) string { func terminationMessage(t *testing.T) string {
b, err := ioutil.ReadFile(terminationLog(t)) b, err := ioutil.ReadFile(terminationLogUnixPath(t))
if err != nil { if err != nil {
t.Log(err) t.Log(err)
} }
@@ -148,7 +186,7 @@ func cleanContainer(t *testing.T, cli *client.Client, ID string) {
if m != "" { if m != "" {
t.Logf("Termination message: %v", m) t.Logf("Termination message: %v", m)
} }
os.Remove(terminationLog(t)) os.Remove(terminationLogUnixPath(t))
t.Logf("Removing container: %s", ID) t.Logf("Removing container: %s", ID)
opts := types.ContainerRemoveOptions{ opts := types.ContainerRemoveOptions{