Merge pull request #20 from arthurbarr/master
Latest build and test changes
This commit is contained in:
@@ -13,17 +13,12 @@ before_install:
|
|||||||
- sudo apt-get update
|
- sudo apt-get update
|
||||||
- sudo apt-get -y install docker-ce
|
- sudo apt-get -y install docker-ce
|
||||||
- curl https://glide.sh/get | sh
|
- curl https://glide.sh/get | sh
|
||||||
- curl -LO https://github.com/golang/dep/releases/download/v0.3.0/dep-linux-amd64.zip
|
- sudo curl -Lo /usr/local/bin/dep https://github.com/golang/dep/releases/download/v0.3.2/dep-linux-amd64
|
||||||
- unzip dep-linux-amd64.zip
|
- sudo chmod +x /usr/local/bin/dep
|
||||||
- sudo mv dep /usr/local/bin
|
|
||||||
- rm dep-linux-amd64.zip
|
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- echo nothing
|
- echo nothing
|
||||||
|
|
||||||
before_script:
|
|
||||||
- make deps
|
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- make build-devserver
|
- make build-devserver
|
||||||
- make test-devserver
|
- make test-devserver
|
||||||
|
|||||||
60
Makefile
60
Makefile
@@ -16,7 +16,7 @@ BUILD_SERVER_CONTAINER=build-server
|
|||||||
DOCKER_TAG_ARCH ?= $(shell uname -m)
|
DOCKER_TAG_ARCH ?= $(shell uname -m)
|
||||||
# By default, all Docker client commands are run inside a Docker container.
|
# By default, all Docker client commands are run inside a Docker container.
|
||||||
# This means that newer features of the client can be used, even with an older daemon.
|
# This means that newer features of the client can be used, even with an older daemon.
|
||||||
DOCKER ?= docker run --tty --interactive --rm --volume /var/run/docker.sock:/var/run/docker.sock --volume "$(CURDIR)":/var/src --workdir /var/src docker:stable docker
|
DOCKER ?= docker
|
||||||
DOCKER_TAG ?= latest-$(DOCKER_TAG_ARCH)
|
DOCKER_TAG ?= latest-$(DOCKER_TAG_ARCH)
|
||||||
DOCKER_REPO_DEVSERVER ?= mq-devserver
|
DOCKER_REPO_DEVSERVER ?= mq-devserver
|
||||||
DOCKER_REPO_ADVANCEDSERVER ?= mq-advancedserver
|
DOCKER_REPO_ADVANCEDSERVER ?= mq-advancedserver
|
||||||
@@ -43,6 +43,14 @@ TEST_OPTS_DOCKER ?=
|
|||||||
# Options to `go test` for the Kubernetes tests
|
# Options to `go test` for the Kubernetes tests
|
||||||
TEST_OPTS_KUBERNETES ?=
|
TEST_OPTS_KUBERNETES ?=
|
||||||
TEST_IMAGE ?= $(DOCKER_FULL_ADVANCEDSERVER)
|
TEST_IMAGE ?= $(DOCKER_FULL_ADVANCEDSERVER)
|
||||||
|
NUM_CPU=$(shell docker info --format "{{ .NCPU }}")
|
||||||
|
|
||||||
|
.PHONY: vars
|
||||||
|
vars:
|
||||||
|
echo $(DOCKER_SERVER_VERSION_MAJOR)
|
||||||
|
echo $(DOCKER_SERVER_VERSION_MINOR)
|
||||||
|
echo $(DOCKER_CLIENT_VERSION_MAJOR)
|
||||||
|
echo $(DOCKER_CLIENT_VERSION_MINOR)
|
||||||
|
|
||||||
.PHONY: default
|
.PHONY: default
|
||||||
default: build-devserver test
|
default: build-devserver test
|
||||||
@@ -75,26 +83,37 @@ downloads: downloads/$(MQ_ARCHIVE_DEV)
|
|||||||
.PHONY: deps
|
.PHONY: deps
|
||||||
deps:
|
deps:
|
||||||
glide install --strip-vendor
|
glide install --strip-vendor
|
||||||
|
|
||||||
|
# Vendor Go dependencies for the Docker tests
|
||||||
|
test/docker/vendor:
|
||||||
|
cd test/docker && dep ensure -vendor-only
|
||||||
|
|
||||||
|
# Vendor Go dependencies for the Kubernetes tests
|
||||||
|
test/kubernetes/vendor:
|
||||||
cd test/docker && dep ensure -vendor-only
|
cd test/docker && dep ensure -vendor-only
|
||||||
cd test/kubernetes && dep ensure -vendor-only
|
|
||||||
|
|
||||||
.PHONY: build-cov
|
.PHONY: build-cov
|
||||||
build-cov:
|
build-cov:
|
||||||
mkdir -p build
|
mkdir -p build
|
||||||
cd build; go test -c -covermode=count ../cmd/runmqserver
|
cd build; go test -c -covermode=count ../cmd/runmqserver
|
||||||
|
|
||||||
|
# Shortcut to just run the unit tests
|
||||||
|
.PHONY: test-unit
|
||||||
|
test-unit:
|
||||||
|
docker build --target builder --file Dockerfile-server .
|
||||||
|
|
||||||
.PHONY: test-advancedserver
|
.PHONY: test-advancedserver
|
||||||
test-advancedserver:
|
test-advancedserver: test/docker/vendor
|
||||||
$(info $(SPACER)$(shell printf $(TITLE)"Test $(DOCKER_FULL_ADVANCEDSERVER) on Docker"$(END)))
|
$(info $(SPACER)$(shell printf $(TITLE)"Test $(DOCKER_FULL_ADVANCEDSERVER) on Docker"$(END)))
|
||||||
cd test/docker && TEST_IMAGE=$(DOCKER_FULL_ADVANCEDSERVER) go test $(TEST_OPTS_DOCKER)
|
cd test/docker && TEST_IMAGE=$(DOCKER_FULL_ADVANCEDSERVER) go test -parallel $(NUM_CPU) $(TEST_OPTS_DOCKER)
|
||||||
|
|
||||||
.PHONY: test-devserver
|
.PHONY: test-devserver
|
||||||
test-devserver:
|
test-devserver: test/docker/vendor
|
||||||
$(info $(SPACER)$(shell printf $(TITLE)"Test $(DOCKER_FULL_DEVSERVER) on Docker"$(END)))
|
$(info $(SPACER)$(shell printf $(TITLE)"Test $(DOCKER_FULL_DEVSERVER) on Docker"$(END)))
|
||||||
cd test/docker && TEST_IMAGE=$(DOCKER_FULL_DEVSERVER) go test
|
cd test/docker && TEST_IMAGE=$(DOCKER_FULL_DEVSERVER) go test -parallel $(NUM_CPU)
|
||||||
|
|
||||||
.PHONY: test-advancedserver-cover
|
.PHONY: test-advancedserver-cover
|
||||||
test-advancedserver-cover:
|
test-advancedserver-cover: test/docker/vendor
|
||||||
$(info $(SPACER)$(shell printf $(TITLE)"Test $(DOCKER_REPO_ADVANCEDSERVER) on Docker with code coverage"$(END)))
|
$(info $(SPACER)$(shell printf $(TITLE)"Test $(DOCKER_REPO_ADVANCEDSERVER) on Docker with code coverage"$(END)))
|
||||||
rm -f ./coverage/unit*.cov
|
rm -f ./coverage/unit*.cov
|
||||||
# Run unit tests with coverage, for each package under 'internal'
|
# Run unit tests with coverage, for each package under 'internal'
|
||||||
@@ -115,11 +134,11 @@ test-advancedserver-cover:
|
|||||||
go tool cover -html=./coverage/combined.cov -o ./coverage/combined.html
|
go tool cover -html=./coverage/combined.cov -o ./coverage/combined.html
|
||||||
|
|
||||||
.PHONY: test-kubernetes-devserver
|
.PHONY: test-kubernetes-devserver
|
||||||
test-kubernetes-devserver:
|
test-kubernetes-devserver: test/kubernetes/vendor
|
||||||
$(call test-kubernetes,$(DOCKER_REPO_DEVSERVER),$(DOCKER_TAG),"../../charts/ibm-mqadvanced-server-dev")
|
$(call test-kubernetes,$(DOCKER_REPO_DEVSERVER),$(DOCKER_TAG),"../../charts/ibm-mqadvanced-server-dev")
|
||||||
|
|
||||||
.PHONY: test-kubernetes-advancedserver
|
.PHONY: test-kubernetes-advancedserver
|
||||||
test-kubernetes-advancedserver:
|
test-kubernetes-advancedserver: test/kubernetes/vendor
|
||||||
$(call test-kubernetes,$(DOCKER_REPO_ADVANCEDSERVER),$(DOCKER_TAG),"../../charts/ibm-mqadvanced-server-prod")
|
$(call test-kubernetes,$(DOCKER_REPO_ADVANCEDSERVER),$(DOCKER_TAG),"../../charts/ibm-mqadvanced-server-prod")
|
||||||
|
|
||||||
define test-kubernetes
|
define test-kubernetes
|
||||||
@@ -139,9 +158,10 @@ define docker-build-mq
|
|||||||
--volume "$(realpath ./downloads/)":/usr/share/nginx/html:ro \
|
--volume "$(realpath ./downloads/)":/usr/share/nginx/html:ro \
|
||||||
--detach \
|
--detach \
|
||||||
nginx:alpine
|
nginx:alpine
|
||||||
|
# Make sure we have the latest base image
|
||||||
|
$(DOCKER) pull ubuntu:16.04
|
||||||
# Build the new image
|
# Build the new image
|
||||||
$(DOCKER) build \
|
$(DOCKER) build \
|
||||||
--pull \
|
|
||||||
--tag $1 \
|
--tag $1 \
|
||||||
--file $2 \
|
--file $2 \
|
||||||
--network build \
|
--network build \
|
||||||
@@ -153,24 +173,30 @@ define docker-build-mq
|
|||||||
. ; $(DOCKER) kill $(BUILD_SERVER_CONTAINER) && $(DOCKER) network rm build
|
. ; $(DOCKER) kill $(BUILD_SERVER_CONTAINER) && $(DOCKER) network rm build
|
||||||
endef
|
endef
|
||||||
|
|
||||||
|
DOCKER_SERVER_VERSION=$(shell docker version --format "{{ .Server.Version }}")
|
||||||
|
DOCKER_CLIENT_VERSION=$(shell docker version --format "{{ .Client.Version }}")
|
||||||
|
.PHONY: docker-version
|
||||||
|
docker-version:
|
||||||
|
@test "$(word 1,$(subst ., ,$(DOCKER_CLIENT_VERSION)))" -ge "17" || (echo "Error: Docker client 17.05 or greater is required" && exit 1)
|
||||||
|
@test "$(word 2,$(subst ., ,$(DOCKER_CLIENT_VERSION)))" -ge "05" || (echo "Error: Docker client 17.05 or greater is required" && exit 1)
|
||||||
|
@test "$(word 1,$(subst ., ,$(DOCKER_SERVER_VERSION)))" -ge "17" || (echo "Error: Docker server 17.05 or greater is required" && exit 1)
|
||||||
|
@test "$(word 2,$(subst ., ,$(DOCKER_SERVER_VERSION)))" -ge "05" || (echo "Error: Docker server 17.05 or greater is required" && exit 1)
|
||||||
|
|
||||||
.PHONY: build-advancedserver
|
.PHONY: build-advancedserver
|
||||||
build-advancedserver: downloads/$(MQ_ARCHIVE)
|
build-advancedserver: downloads/$(MQ_ARCHIVE) docker-version
|
||||||
$(info $(SPACER)$(shell printf $(TITLE)"Build $(DOCKER_FULL_ADVANCEDSERVER)"$(END)))
|
$(info $(SPACER)$(shell printf $(TITLE)"Build $(DOCKER_FULL_ADVANCEDSERVER)"$(END)))
|
||||||
$(call docker-build-mq,$(DOCKER_FULL_ADVANCEDSERVER),Dockerfile-server,$(MQ_ARCHIVE),"4486e8c4cc9146fd9b3ce1f14a2dfc5b","IBM MQ Advanced",$(MQ_VERSION))
|
$(call docker-build-mq,$(DOCKER_FULL_ADVANCEDSERVER),Dockerfile-server,$(MQ_ARCHIVE),"4486e8c4cc9146fd9b3ce1f14a2dfc5b","IBM MQ Advanced",$(MQ_VERSION))
|
||||||
$(DOCKER) tag $(DOCKER_FULL_ADVANCEDSERVER) $(DOCKER_REPO_ADVANCEDSERVER):$(MQ_VERSION)-$(DOCKER_TAG_ARCH)
|
$(DOCKER) tag $(DOCKER_FULL_ADVANCEDSERVER) $(DOCKER_REPO_ADVANCEDSERVER):$(MQ_VERSION)-$(DOCKER_TAG_ARCH)
|
||||||
|
|
||||||
.PHONY: build-devserver
|
.PHONY: build-devserver
|
||||||
build-devserver: downloads/$(MQ_ARCHIVE_DEV)
|
build-devserver: downloads/$(MQ_ARCHIVE_DEV) docker-version
|
||||||
ifneq "x86_64" "$(shell uname -m)"
|
@test "$(shell uname -m)" = "x86_64" || (echo "Error: MQ Advanced for Developers is only available for x86_64 architecture" && exit 1)
|
||||||
$(error MQ Advanced for Developers is only available for x86_64 architecture)
|
|
||||||
else
|
|
||||||
$(info $(shell printf $(TITLE)"Build $(DOCKER_FULL_DEVSERVER)"$(END)))
|
$(info $(shell printf $(TITLE)"Build $(DOCKER_FULL_DEVSERVER)"$(END)))
|
||||||
$(call docker-build-mq,$(DOCKER_FULL_DEVSERVER),Dockerfile-server,$(MQ_ARCHIVE_DEV),"98102d16795c4263ad9ca075190a2d4d","IBM MQ Advanced for Developers (Non-Warranted)",$(MQ_VERSION))
|
$(call docker-build-mq,$(DOCKER_FULL_DEVSERVER),Dockerfile-server,$(MQ_ARCHIVE_DEV),"98102d16795c4263ad9ca075190a2d4d","IBM MQ Advanced for Developers (Non-Warranted)",$(MQ_VERSION))
|
||||||
$(DOCKER) tag $(DOCKER_FULL_DEVSERVER) $(DOCKER_REPO_DEVSERVER):$(MQ_VERSION)-$(DOCKER_TAG_ARCH)
|
$(DOCKER) tag $(DOCKER_FULL_DEVSERVER) $(DOCKER_REPO_DEVSERVER):$(MQ_VERSION)-$(DOCKER_TAG_ARCH)
|
||||||
endif
|
|
||||||
|
|
||||||
.PHONY: build-advancedserver-cover
|
.PHONY: build-advancedserver-cover
|
||||||
build-advancedserver-cover:
|
build-advancedserver-cover: docker-version
|
||||||
$(DOCKER) build -t $(DOCKER_REPO_ADVANCEDSERVER):cover -f Dockerfile-server.cover .
|
$(DOCKER) build -t $(DOCKER_REPO_ADVANCEDSERVER):cover -f Dockerfile-server.cover .
|
||||||
|
|
||||||
# .PHONY: build-web
|
# .PHONY: build-web
|
||||||
|
|||||||
@@ -36,7 +36,8 @@ func resolveLicenseFile() string {
|
|||||||
return "Chinese_TW.txt"
|
return "Chinese_TW.txt"
|
||||||
case strings.HasPrefix(lang, "zh"):
|
case strings.HasPrefix(lang, "zh"):
|
||||||
return "Chinese.txt"
|
return "Chinese.txt"
|
||||||
case strings.HasPrefix(lang, "cs"):
|
// Differentiate Czech (cs) and Kashubian (csb)
|
||||||
|
case strings.HasPrefix(lang, "cs") && !strings.HasPrefix(lang, "csb"):
|
||||||
return "Czech.txt"
|
return "Czech.txt"
|
||||||
case strings.HasPrefix(lang, "fr"):
|
case strings.HasPrefix(lang, "fr"):
|
||||||
return "French.txt"
|
return "French.txt"
|
||||||
@@ -50,7 +51,8 @@ func resolveLicenseFile() string {
|
|||||||
return "Italian.txt"
|
return "Italian.txt"
|
||||||
case strings.HasPrefix(lang, "ja"):
|
case strings.HasPrefix(lang, "ja"):
|
||||||
return "Japanese.txt"
|
return "Japanese.txt"
|
||||||
case strings.HasPrefix(lang, "ko"):
|
// Differentiate Korean (ko) from Konkani (kok)
|
||||||
|
case strings.HasPrefix(lang, "ko") && !strings.HasPrefix(lang, "kok"):
|
||||||
return "Korean.txt"
|
return "Korean.txt"
|
||||||
case strings.HasPrefix(lang, "lt"):
|
case strings.HasPrefix(lang, "lt"):
|
||||||
return "Lithuanian.txt"
|
return "Lithuanian.txt"
|
||||||
|
|||||||
282
cmd/runmqserver/license_test.go
Normal file
282
cmd/runmqserver/license_test.go
Normal file
@@ -0,0 +1,282 @@
|
|||||||
|
/*
|
||||||
|
© Copyright IBM Corporation 2017
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE_2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var licenseTests = []struct {
|
||||||
|
in string
|
||||||
|
out string
|
||||||
|
}{
|
||||||
|
{"en_US.UTF_8", "English.txt"},
|
||||||
|
{"en_US.ISO-8859-15", "English.txt"},
|
||||||
|
{"es_GB", "Spanish.txt"},
|
||||||
|
{"el_ES.UTF_8", "Greek.txt"},
|
||||||
|
// Cover a wide variety of valid values
|
||||||
|
{"af", "English.txt"},
|
||||||
|
{"af_ZA", "English.txt"},
|
||||||
|
{"ar", "English.txt"},
|
||||||
|
{"ar_AE", "English.txt"},
|
||||||
|
{"ar_BH", "English.txt"},
|
||||||
|
{"ar_DZ", "English.txt"},
|
||||||
|
{"ar_EG", "English.txt"},
|
||||||
|
{"ar_IQ", "English.txt"},
|
||||||
|
{"ar_JO", "English.txt"},
|
||||||
|
{"ar_KW", "English.txt"},
|
||||||
|
{"ar_LB", "English.txt"},
|
||||||
|
{"ar_LY", "English.txt"},
|
||||||
|
{"ar_MA", "English.txt"},
|
||||||
|
{"ar_OM", "English.txt"},
|
||||||
|
{"ar_QA", "English.txt"},
|
||||||
|
{"ar_SA", "English.txt"},
|
||||||
|
{"ar_SY", "English.txt"},
|
||||||
|
{"ar_TN", "English.txt"},
|
||||||
|
{"ar_YE", "English.txt"},
|
||||||
|
{"az", "English.txt"},
|
||||||
|
{"az_AZ", "English.txt"},
|
||||||
|
{"az_AZ", "English.txt"},
|
||||||
|
{"be", "English.txt"},
|
||||||
|
{"be_BY", "English.txt"},
|
||||||
|
{"bg", "English.txt"},
|
||||||
|
{"bg_BG", "English.txt"},
|
||||||
|
{"bs_BA", "English.txt"},
|
||||||
|
{"ca", "English.txt"},
|
||||||
|
{"ca_ES", "English.txt"},
|
||||||
|
{"cs", "Czech.txt"},
|
||||||
|
{"cs_CZ", "Czech.txt"},
|
||||||
|
{"csb_PL", "English.txt"},
|
||||||
|
{"cy", "English.txt"},
|
||||||
|
{"cy_GB", "English.txt"},
|
||||||
|
{"da", "English.txt"},
|
||||||
|
{"da_DK", "English.txt"},
|
||||||
|
{"de", "German.txt"},
|
||||||
|
{"de_AT", "German.txt"},
|
||||||
|
{"de_CH", "German.txt"},
|
||||||
|
{"de_DE", "German.txt"},
|
||||||
|
{"de_LI", "German.txt"},
|
||||||
|
{"de_LU", "German.txt"},
|
||||||
|
{"dv", "English.txt"},
|
||||||
|
{"dv_MV", "English.txt"},
|
||||||
|
{"el", "Greek.txt"},
|
||||||
|
{"el_GR", "Greek.txt"},
|
||||||
|
{"en", "English.txt"},
|
||||||
|
{"en_AU", "English.txt"},
|
||||||
|
{"en_BZ", "English.txt"},
|
||||||
|
{"en_CA", "English.txt"},
|
||||||
|
{"en_CB", "English.txt"},
|
||||||
|
{"en_GB", "English.txt"},
|
||||||
|
{"en_IE", "English.txt"},
|
||||||
|
{"en_JM", "English.txt"},
|
||||||
|
{"en_NZ", "English.txt"},
|
||||||
|
{"en_PH", "English.txt"},
|
||||||
|
{"en_TT", "English.txt"},
|
||||||
|
{"en_US", "English.txt"},
|
||||||
|
{"en_ZA", "English.txt"},
|
||||||
|
{"en_ZW", "English.txt"},
|
||||||
|
{"eo", "English.txt"},
|
||||||
|
{"es", "Spanish.txt"},
|
||||||
|
{"es_AR", "Spanish.txt"},
|
||||||
|
{"es_BO", "Spanish.txt"},
|
||||||
|
{"es_CL", "Spanish.txt"},
|
||||||
|
{"es_CO", "Spanish.txt"},
|
||||||
|
{"es_CR", "Spanish.txt"},
|
||||||
|
{"es_DO", "Spanish.txt"},
|
||||||
|
{"es_EC", "Spanish.txt"},
|
||||||
|
{"es_ES", "Spanish.txt"},
|
||||||
|
{"es_ES", "Spanish.txt"},
|
||||||
|
{"es_GT", "Spanish.txt"},
|
||||||
|
{"es_HN", "Spanish.txt"},
|
||||||
|
{"es_MX", "Spanish.txt"},
|
||||||
|
{"es_NI", "Spanish.txt"},
|
||||||
|
{"es_PA", "Spanish.txt"},
|
||||||
|
{"es_PE", "Spanish.txt"},
|
||||||
|
{"es_PR", "Spanish.txt"},
|
||||||
|
{"es_PY", "Spanish.txt"},
|
||||||
|
{"es_SV", "Spanish.txt"},
|
||||||
|
{"es_UY", "Spanish.txt"},
|
||||||
|
{"es_VE", "Spanish.txt"},
|
||||||
|
{"et", "English.txt"},
|
||||||
|
{"et_EE", "English.txt"},
|
||||||
|
{"eu", "English.txt"},
|
||||||
|
{"eu_ES", "English.txt"},
|
||||||
|
{"fa", "English.txt"},
|
||||||
|
{"fa_IR", "English.txt"},
|
||||||
|
{"fi", "English.txt"},
|
||||||
|
{"fi_FI", "English.txt"},
|
||||||
|
{"fo", "English.txt"},
|
||||||
|
{"fo_FO", "English.txt"},
|
||||||
|
{"fr", "French.txt"},
|
||||||
|
{"fr_BE", "French.txt"},
|
||||||
|
{"fr_CA", "French.txt"},
|
||||||
|
{"fr_CH", "French.txt"},
|
||||||
|
{"fr_FR", "French.txt"},
|
||||||
|
{"fr_LU", "French.txt"},
|
||||||
|
{"fr_MC", "French.txt"},
|
||||||
|
{"gl", "English.txt"},
|
||||||
|
{"gl_ES", "English.txt"},
|
||||||
|
{"gu", "English.txt"},
|
||||||
|
{"gu_IN", "English.txt"},
|
||||||
|
{"he", "English.txt"},
|
||||||
|
{"he_IL", "English.txt"},
|
||||||
|
{"hi", "English.txt"},
|
||||||
|
{"hi_IN", "English.txt"},
|
||||||
|
{"hr", "English.txt"},
|
||||||
|
{"hr_BA", "English.txt"},
|
||||||
|
{"hr_HR", "English.txt"},
|
||||||
|
{"hu", "English.txt"},
|
||||||
|
{"hu_HU", "English.txt"},
|
||||||
|
{"hy", "English.txt"},
|
||||||
|
{"hy_AM", "English.txt"},
|
||||||
|
{"id", "Indonesian.txt"},
|
||||||
|
{"id_ID", "Indonesian.txt"},
|
||||||
|
{"is", "English.txt"},
|
||||||
|
{"is_IS", "English.txt"},
|
||||||
|
{"it", "Italian.txt"},
|
||||||
|
{"it_CH", "Italian.txt"},
|
||||||
|
{"it_IT", "Italian.txt"},
|
||||||
|
{"ja", "Japanese.txt"},
|
||||||
|
{"ja_JP", "Japanese.txt"},
|
||||||
|
{"ka", "English.txt"},
|
||||||
|
{"ka_GE", "English.txt"},
|
||||||
|
{"kk", "English.txt"},
|
||||||
|
{"kk_KZ", "English.txt"},
|
||||||
|
{"kn", "English.txt"},
|
||||||
|
{"kn_IN", "English.txt"},
|
||||||
|
{"ko", "Korean.txt"},
|
||||||
|
{"ko_KR", "Korean.txt"},
|
||||||
|
{"kok", "English.txt"},
|
||||||
|
{"kok_IN", "English.txt"},
|
||||||
|
{"ky", "English.txt"},
|
||||||
|
{"ky_KG", "English.txt"},
|
||||||
|
{"lt", "Lithuanian.txt"},
|
||||||
|
{"lt_LT", "Lithuanian.txt"},
|
||||||
|
{"lv", "English.txt"},
|
||||||
|
{"lv_LV", "English.txt"},
|
||||||
|
{"mi", "English.txt"},
|
||||||
|
{"mi_NZ", "English.txt"},
|
||||||
|
{"mk", "English.txt"},
|
||||||
|
{"mk_MK", "English.txt"},
|
||||||
|
{"mn", "English.txt"},
|
||||||
|
{"mn_MN", "English.txt"},
|
||||||
|
{"mr", "English.txt"},
|
||||||
|
{"mr_IN", "English.txt"},
|
||||||
|
{"ms", "English.txt"},
|
||||||
|
{"ms_BN", "English.txt"},
|
||||||
|
{"ms_MY", "English.txt"},
|
||||||
|
{"mt", "English.txt"},
|
||||||
|
{"mt_MT", "English.txt"},
|
||||||
|
{"nb", "English.txt"},
|
||||||
|
{"nb_NO", "English.txt"},
|
||||||
|
{"nl", "English.txt"},
|
||||||
|
{"nl_BE", "English.txt"},
|
||||||
|
{"nl_NL", "English.txt"},
|
||||||
|
{"nn_NO", "English.txt"},
|
||||||
|
{"ns", "English.txt"},
|
||||||
|
{"ns_ZA", "English.txt"},
|
||||||
|
{"pa", "English.txt"},
|
||||||
|
{"pa_IN", "English.txt"},
|
||||||
|
{"pl", "Polish.txt"},
|
||||||
|
{"pl_PL", "Polish.txt"},
|
||||||
|
{"ps", "English.txt"},
|
||||||
|
{"ps_AR", "English.txt"},
|
||||||
|
{"pt", "Portugese.txt"},
|
||||||
|
{"pt_BR", "Portugese.txt"},
|
||||||
|
{"pt_PT", "Portugese.txt"},
|
||||||
|
{"qu", "English.txt"},
|
||||||
|
{"qu_BO", "English.txt"},
|
||||||
|
{"qu_EC", "English.txt"},
|
||||||
|
{"qu_PE", "English.txt"},
|
||||||
|
{"ro", "English.txt"},
|
||||||
|
{"ro_RO", "English.txt"},
|
||||||
|
{"ru", "Russian.txt"},
|
||||||
|
{"ru_RU", "Russian.txt"},
|
||||||
|
{"sa", "English.txt"},
|
||||||
|
{"sa_IN", "English.txt"},
|
||||||
|
{"se", "English.txt"},
|
||||||
|
{"se_FI", "English.txt"},
|
||||||
|
{"se_FI", "English.txt"},
|
||||||
|
{"se_FI", "English.txt"},
|
||||||
|
{"se_NO", "English.txt"},
|
||||||
|
{"se_NO", "English.txt"},
|
||||||
|
{"se_NO", "English.txt"},
|
||||||
|
{"se_SE", "English.txt"},
|
||||||
|
{"se_SE", "English.txt"},
|
||||||
|
{"se_SE", "English.txt"},
|
||||||
|
{"sk", "English.txt"},
|
||||||
|
{"sk_SK", "English.txt"},
|
||||||
|
{"sl", "Slovenian.txt"},
|
||||||
|
{"sl_SI", "Slovenian.txt"},
|
||||||
|
{"sq", "English.txt"},
|
||||||
|
{"sq_AL", "English.txt"},
|
||||||
|
{"sr_BA", "English.txt"},
|
||||||
|
{"sr_BA", "English.txt"},
|
||||||
|
{"sr_SP", "English.txt"},
|
||||||
|
{"sr_SP", "English.txt"},
|
||||||
|
{"sv", "English.txt"},
|
||||||
|
{"sv_FI", "English.txt"},
|
||||||
|
{"sv_SE", "English.txt"},
|
||||||
|
{"sw", "English.txt"},
|
||||||
|
{"sw_KE", "English.txt"},
|
||||||
|
{"syr", "English.txt"},
|
||||||
|
{"syr_SY", "English.txt"},
|
||||||
|
{"ta", "English.txt"},
|
||||||
|
{"ta_IN", "English.txt"},
|
||||||
|
{"te", "English.txt"},
|
||||||
|
{"te_IN", "English.txt"},
|
||||||
|
{"th", "English.txt"},
|
||||||
|
{"th_TH", "English.txt"},
|
||||||
|
{"tl", "English.txt"},
|
||||||
|
{"tl_PH", "English.txt"},
|
||||||
|
{"tn", "English.txt"},
|
||||||
|
{"tn_ZA", "English.txt"},
|
||||||
|
{"tr", "Turkish.txt"},
|
||||||
|
{"tr_TR", "Turkish.txt"},
|
||||||
|
{"tt", "English.txt"},
|
||||||
|
{"tt_RU", "English.txt"},
|
||||||
|
{"ts", "English.txt"},
|
||||||
|
{"uk", "English.txt"},
|
||||||
|
{"uk_UA", "English.txt"},
|
||||||
|
{"ur", "English.txt"},
|
||||||
|
{"ur_PK", "English.txt"},
|
||||||
|
{"uz", "English.txt"},
|
||||||
|
{"uz_UZ", "English.txt"},
|
||||||
|
{"uz_UZ", "English.txt"},
|
||||||
|
{"vi", "English.txt"},
|
||||||
|
{"vi_VN", "English.txt"},
|
||||||
|
{"xh", "English.txt"},
|
||||||
|
{"xh_ZA", "English.txt"},
|
||||||
|
{"zh", "Chinese.txt"},
|
||||||
|
{"zh_CN", "Chinese.txt"},
|
||||||
|
{"zh_HK", "Chinese.txt"},
|
||||||
|
{"zh_MO", "Chinese.txt"},
|
||||||
|
{"zh_SG", "Chinese.txt"},
|
||||||
|
{"zh_TW", "Chinese_TW.txt"},
|
||||||
|
{"zu", "English.txt"},
|
||||||
|
{"zu_ZA", "English.txt"},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestResolveLicenseFile(t *testing.T) {
|
||||||
|
for _, table := range licenseTests {
|
||||||
|
os.Setenv("LANG", table.in)
|
||||||
|
f := resolveLicenseFile()
|
||||||
|
if f != table.out {
|
||||||
|
t.Errorf("resolveLicenseFile() with LANG=%v - expected %v, got %v", table.in, table.out, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,6 +19,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
@@ -30,6 +31,20 @@ import (
|
|||||||
"github.com/ibm-messaging/mq-container/internal/name"
|
"github.com/ibm-messaging/mq-container/internal/name"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var debug = false
|
||||||
|
|
||||||
|
func logDebug(msg string) {
|
||||||
|
if debug {
|
||||||
|
log.Printf("DEBUG: %v", msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func logDebugf(format string, args ...interface{}) {
|
||||||
|
if debug {
|
||||||
|
log.Printf("DEBUG: %v", fmt.Sprintf(format, args...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// createDirStructure creates the default MQ directory structure under /var/mqm
|
// createDirStructure creates the default MQ directory structure under /var/mqm
|
||||||
func createDirStructure() error {
|
func createDirStructure() error {
|
||||||
out, _, err := command.Run("/opt/mqm/bin/crtmqdir", "-f", "-s")
|
out, _, err := command.Run("/opt/mqm/bin/crtmqdir", "-f", "-s")
|
||||||
@@ -127,6 +142,10 @@ func stopQueueManager(name string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func doMain() error {
|
func doMain() error {
|
||||||
|
debugEnv, ok := os.LookupEnv("DEBUG")
|
||||||
|
if ok && (debugEnv == "true" || debugEnv == "1") {
|
||||||
|
debug = true
|
||||||
|
}
|
||||||
accepted, err := checkLicense()
|
accepted, err := checkLicense()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -50,11 +50,13 @@ func signalHandler(qmgr string) chan int {
|
|||||||
// End the goroutine
|
// End the goroutine
|
||||||
return
|
return
|
||||||
case <-reapSignals:
|
case <-reapSignals:
|
||||||
|
logDebug("Received SIGCHLD signal")
|
||||||
reapZombies()
|
reapZombies()
|
||||||
case job := <-control:
|
case job := <-control:
|
||||||
switch {
|
switch {
|
||||||
case job == startReaping:
|
case job == startReaping:
|
||||||
// Add SIGCHLD to the list of signals we're listening to
|
// Add SIGCHLD to the list of signals we're listening to
|
||||||
|
logDebug("Listening for SIGCHLD signals")
|
||||||
signal.Notify(reapSignals, syscall.SIGCHLD)
|
signal.Notify(reapSignals, syscall.SIGCHLD)
|
||||||
case job == reapNow:
|
case job == reapNow:
|
||||||
reapZombies()
|
reapZombies()
|
||||||
@@ -75,5 +77,6 @@ func reapZombies() {
|
|||||||
if pid == 0 || err == unix.ECHILD {
|
if pid == 0 || err == unix.ECHILD {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
logDebugf("Reaped PID %v", pid)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,12 +2,13 @@
|
|||||||
|
|
||||||
## 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.05 or later
|
||||||
|
* [GNU make](https://www.gnu.org/software/make/)
|
||||||
|
|
||||||
* [Docker](https://www.docker.com/)
|
You might also need the following tools installed:
|
||||||
* [Go](https://golang.org/) - only needed for running the tests
|
* [Go](https://golang.org/) - only needed for running the tests
|
||||||
* [Glide](https://glide.sh/)
|
* [Glide](https://glide.sh/) - only needed if you update the main dependencies
|
||||||
* [dep](https://github.com/golang/dep) (official Go dependency management tool)
|
* [dep](https://github.com/golang/dep) (official Go dependency management tool) - only needed to prepare for running the tests
|
||||||
* make
|
|
||||||
* [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/).
|
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/).
|
||||||
@@ -24,6 +25,12 @@ You can build a different version of MQ by setting the `MQ_VERSION` environment
|
|||||||
MQ_VERSION=9.0.3.0 make build-advancedserver
|
MQ_VERSION=9.0.3.0 make build-advancedserver
|
||||||
```
|
```
|
||||||
|
|
||||||
|
If you have an MQ archive file with a different file name, you can specify a particular file (which must be in the `downloads` directory). You should also specify the MQ version, so that the resulting image is tagged correctly, for example:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
MQ_ARCHIVE=mq-1.2.3.4.tar.gz MQ_VERSION=1.2.3.4 build-advancedserver
|
||||||
|
```
|
||||||
|
|
||||||
## Running the tests
|
## Running the tests
|
||||||
There are three main sets of tests:
|
There are three main sets of tests:
|
||||||
|
|
||||||
@@ -50,6 +57,12 @@ or:
|
|||||||
make test-advancedserver
|
make test-advancedserver
|
||||||
```
|
```
|
||||||
|
|
||||||
|
You can pass parameters to `go test` with an environment variable. For example, to run the "TestGoldenPath" test, run the following command::
|
||||||
|
|
||||||
|
```
|
||||||
|
TEST_OPTS_DOCKER="-run TestGoldenPath" make test-advancedserver
|
||||||
|
```
|
||||||
|
|
||||||
### Running the Docker tests with code coverage
|
### Running the Docker tests with code coverage
|
||||||
You can produce code coverage results from the Docker tests by running the following:
|
You can produce code coverage results from the Docker tests by running the following:
|
||||||
|
|
||||||
|
|||||||
@@ -87,12 +87,10 @@ find /opt/mqm -name '*.tar.gz' -delete
|
|||||||
rm -f /etc/apt/sources.list.d/IBM_MQ.list
|
rm -f /etc/apt/sources.list.d/IBM_MQ.list
|
||||||
rm -rf ${DIR_EXTRACT}
|
rm -rf ${DIR_EXTRACT}
|
||||||
|
|
||||||
# Apply any bug fixes not included in base Ubuntu or MQ image.
|
#### Apply any bug fixes not included in base Ubuntu or MQ image.
|
||||||
# Don't upgrade everything based on Docker best practices https://docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices/#run
|
# Don't upgrade everything based on Docker best practices https://docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices/#run
|
||||||
apt-get upgrade -y libkrb5-26-heimdal
|
apt-get upgrade -y libdb5.3
|
||||||
apt-get upgrade -y libexpat1
|
#### End of bug fixes
|
||||||
|
|
||||||
# End of bug fixes
|
|
||||||
|
|
||||||
# Clean up cached apt files
|
# Clean up cached apt files
|
||||||
rm -rf /var/lib/apt/lists/*
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
|||||||
@@ -17,8 +17,10 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/docker/docker/api/types/container"
|
"github.com/docker/docker/api/types/container"
|
||||||
"github.com/docker/docker/api/types/network"
|
"github.com/docker/docker/api/types/network"
|
||||||
@@ -27,6 +29,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestLicenseNotSet(t *testing.T) {
|
func TestLicenseNotSet(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
cli, err := client.NewEnvClient()
|
cli, err := client.NewEnvClient()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -41,6 +44,7 @@ func TestLicenseNotSet(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestLicenseView(t *testing.T) {
|
func TestLicenseView(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
cli, err := client.NewEnvClient()
|
cli, err := client.NewEnvClient()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -61,23 +65,50 @@ func TestLicenseView(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestGoldenPath starts a queue manager successfully
|
||||||
func TestGoldenPath(t *testing.T) {
|
func TestGoldenPath(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
cli, err := client.NewEnvClient()
|
cli, err := client.NewEnvClient()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
containerConfig := container.Config{
|
containerConfig := container.Config{
|
||||||
Env: []string{"LICENSE=accept", "MQ_QMGR_NAME=qm1"},
|
Env: []string{"LICENSE=accept", "MQ_QMGR_NAME=qm1"},
|
||||||
//ExposedPorts: ports,
|
// ExposedPorts: nat.PortSet{
|
||||||
ExposedPorts: nat.PortSet{
|
// "1414/tcp": struct{}{},
|
||||||
"1414/tcp": struct{}{},
|
// },
|
||||||
},
|
|
||||||
}
|
}
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestSecurityVulnerabilities checks for any vulnerabilities in the image, as reported
|
||||||
|
// by Ubuntu
|
||||||
|
func TestSecurityVulnerabilities(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
cli, err := client.NewEnvClient()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
containerConfig := container.Config{
|
||||||
|
// Override the entrypoint to make "apt" only receive security updates, then check for updates
|
||||||
|
Entrypoint: []string{"bash", "-c", "source /etc/os-release && echo \"deb http://security.ubuntu.com/ubuntu/ ${VERSION_CODENAME}-security main restricted\" > /etc/apt/sources.list && apt-get update 2>&1 >/dev/null && apt-get --simulate -qq upgrade"},
|
||||||
|
}
|
||||||
|
id := runContainer(t, cli, &containerConfig)
|
||||||
|
defer cleanContainer(t, cli, id)
|
||||||
|
// rc is the return code from apt-get
|
||||||
|
rc := waitForContainer(t, cli, id, 10)
|
||||||
|
if rc != 0 {
|
||||||
|
t.Fatalf("Expected success, got %v", rc)
|
||||||
|
}
|
||||||
|
log := inspectLogs(t, cli, id)
|
||||||
|
lines := strings.Split(strings.TrimSpace(log), "\n")
|
||||||
|
if len(lines) > 0 && lines[0] != "" {
|
||||||
|
t.Errorf("Expected no vulnerabilities, found the following:\n%v", log)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func utilTestNoQueueManagerName(t *testing.T, hostName string, expectedName string) {
|
func utilTestNoQueueManagerName(t *testing.T, hostName string, expectedName string) {
|
||||||
search := "QMNAME(" + expectedName + ")"
|
search := "QMNAME(" + expectedName + ")"
|
||||||
cli, err := client.NewEnvClient()
|
cli, err := client.NewEnvClient()
|
||||||
@@ -87,29 +118,29 @@ func utilTestNoQueueManagerName(t *testing.T, hostName string, expectedName stri
|
|||||||
containerConfig := container.Config{
|
containerConfig := container.Config{
|
||||||
Env: []string{"LICENSE=accept"},
|
Env: []string{"LICENSE=accept"},
|
||||||
Hostname: hostName,
|
Hostname: hostName,
|
||||||
ExposedPorts: nat.PortSet{
|
|
||||||
"1414/tcp": struct{}{},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
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)
|
||||||
_, out := execContainer(t, cli, id, []string{"dspmq"})
|
out := execContainerWithOutput(t, cli, id, "mqm", []string{"dspmq"})
|
||||||
if !strings.Contains(out, search) {
|
if !strings.Contains(out, search) {
|
||||||
t.Errorf("Expected result of running dspmq to contain name=%v, got name=%v", search, out)
|
t.Errorf("Expected result of running dspmq to contain name=%v, got name=%v", search, out)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
func TestNoQueueManagerName(t *testing.T) {
|
func TestNoQueueManagerName(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
utilTestNoQueueManagerName(t, "test", "test")
|
utilTestNoQueueManagerName(t, "test", "test")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNoQueueManagerNameInvalidHostname(t *testing.T) {
|
func TestNoQueueManagerNameInvalidHostname(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
utilTestNoQueueManagerName(t, "test-1", "test1")
|
utilTestNoQueueManagerName(t, "test-1", "test1")
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestWithVolume runs a container with a Docker volume, then removes that
|
// TestWithVolume runs a container with a Docker volume, then removes that
|
||||||
// container and starts a new one with same volume.
|
// container and starts a new one with same volume.
|
||||||
func TestWithVolume(t *testing.T) {
|
func TestWithVolume(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
cli, err := client.NewEnvClient()
|
cli, err := client.NewEnvClient()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -148,17 +179,16 @@ func TestWithVolume(t *testing.T) {
|
|||||||
waitForReady(t, cli, ctr2.ID)
|
waitForReady(t, cli, ctr2.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestNoVolumeWithRestart ensures a queue manager container can be stopped
|
||||||
|
// and restarted cleanly
|
||||||
func TestNoVolumeWithRestart(t *testing.T) {
|
func TestNoVolumeWithRestart(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
cli, err := client.NewEnvClient()
|
cli, err := client.NewEnvClient()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
containerConfig := container.Config{
|
containerConfig := container.Config{
|
||||||
Env: []string{"LICENSE=accept", "MQ_QMGR_NAME=qm1"},
|
Env: []string{"LICENSE=accept", "MQ_QMGR_NAME=qm1"},
|
||||||
//ExposedPorts: ports,
|
|
||||||
ExposedPorts: nat.PortSet{
|
|
||||||
"1414/tcp": struct{}{},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
id := runContainer(t, cli, &containerConfig)
|
id := runContainer(t, cli, &containerConfig)
|
||||||
defer cleanContainer(t, cli, id)
|
defer cleanContainer(t, cli, id)
|
||||||
@@ -168,8 +198,9 @@ func TestNoVolumeWithRestart(t *testing.T) {
|
|||||||
waitForReady(t, cli, id)
|
waitForReady(t, cli, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test the case where `crtmqm` will fail
|
// TestCreateQueueManagerFail causes a failure of `crtmqm`
|
||||||
func TestCreateQueueManagerFail(t *testing.T) {
|
func TestCreateQueueManagerFail(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
cli, err := client.NewEnvClient()
|
cli, err := client.NewEnvClient()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -178,10 +209,6 @@ func TestCreateQueueManagerFail(t *testing.T) {
|
|||||||
oldEntrypoint := strings.Join(img.Config.Entrypoint, " ")
|
oldEntrypoint := strings.Join(img.Config.Entrypoint, " ")
|
||||||
containerConfig := container.Config{
|
containerConfig := container.Config{
|
||||||
Env: []string{"LICENSE=accept", "MQ_QMGR_NAME=qm1"},
|
Env: []string{"LICENSE=accept", "MQ_QMGR_NAME=qm1"},
|
||||||
//ExposedPorts: ports,
|
|
||||||
ExposedPorts: nat.PortSet{
|
|
||||||
"1414/tcp": struct{}{},
|
|
||||||
},
|
|
||||||
// Override the entrypoint to create the queue manager directory, but leave it empty.
|
// Override the entrypoint to create the queue manager directory, but leave it empty.
|
||||||
// This will cause `crtmqm` to return with an exit code of 2.
|
// This will cause `crtmqm` to return with an exit code of 2.
|
||||||
Entrypoint: []string{"bash", "-c", "mkdir -p /mnt/mqm/data && mkdir -p /var/mqm/qmgrs/qm1 && exec " + oldEntrypoint},
|
Entrypoint: []string{"bash", "-c", "mkdir -p /mnt/mqm/data && mkdir -p /var/mqm/qmgrs/qm1 && exec " + oldEntrypoint},
|
||||||
@@ -194,8 +221,9 @@ func TestCreateQueueManagerFail(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test the case where `strmqm` will fail
|
// TestStartQueueManagerFail causes a failure of `strmqm`
|
||||||
func TestStartQueueManagerFail(t *testing.T) {
|
func TestStartQueueManagerFail(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
cli, err := client.NewEnvClient()
|
cli, err := client.NewEnvClient()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -204,10 +232,6 @@ func TestStartQueueManagerFail(t *testing.T) {
|
|||||||
oldEntrypoint := strings.Join(img.Config.Entrypoint, " ")
|
oldEntrypoint := strings.Join(img.Config.Entrypoint, " ")
|
||||||
containerConfig := container.Config{
|
containerConfig := container.Config{
|
||||||
Env: []string{"LICENSE=accept", "MQ_QMGR_NAME=qm1"},
|
Env: []string{"LICENSE=accept", "MQ_QMGR_NAME=qm1"},
|
||||||
//ExposedPorts: ports,
|
|
||||||
ExposedPorts: nat.PortSet{
|
|
||||||
"1414/tcp": struct{}{},
|
|
||||||
},
|
|
||||||
// Override the entrypoint to replace `crtmqm` with a no-op script.
|
// Override the entrypoint to replace `crtmqm` with a no-op script.
|
||||||
// This will cause `strmqm` to return with an exit code of 16.
|
// This will cause `strmqm` to return with an exit code of 16.
|
||||||
Entrypoint: []string{"bash", "-c", "echo '#!/bin/bash\n' > /opt/mqm/bin/crtmqm && exec " + oldEntrypoint},
|
Entrypoint: []string{"bash", "-c", "echo '#!/bin/bash\n' > /opt/mqm/bin/crtmqm && exec " + oldEntrypoint},
|
||||||
@@ -219,3 +243,87 @@ func TestStartQueueManagerFail(t *testing.T) {
|
|||||||
t.Errorf("Expected rc=1, got rc=%v", rc)
|
t.Errorf("Expected rc=1, got rc=%v", rc)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestVolumeUnmount runs a queue manager with a volume, and then forces an
|
||||||
|
// unmount of the volume. The health check should then fail.
|
||||||
|
// This simulates behaviour seen in some cloud environments, where network
|
||||||
|
// attached storage gets unmounted.
|
||||||
|
func TestVolumeUnmount(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
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"}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestZombies starts a queue manager, then causes a zombie process to be
|
||||||
|
// created, then checks that no zombies exist (runmqserver should reap them)
|
||||||
|
func TestZombies(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", "DEBUG=true"},
|
||||||
|
//ExposedPorts: ports,
|
||||||
|
ExposedPorts: nat.PortSet{
|
||||||
|
"1414/tcp": struct{}{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
id := runContainer(t, cli, &containerConfig)
|
||||||
|
defer cleanContainer(t, cli, id)
|
||||||
|
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"})
|
||||||
|
if out == "0" {
|
||||||
|
t.Fatalf("Expected pkill to kill a process, got %v", out)
|
||||||
|
}
|
||||||
|
time.Sleep(3 * time.Second)
|
||||||
|
// Create a zombie process for up to ten seconds
|
||||||
|
out = execContainerWithOutput(t, cli, id, "mqm", []string{"bash", "-c", "ps -lA | grep '^. Z' | wc -l"})
|
||||||
|
count, err := strconv.Atoi(out)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if count != 0 {
|
||||||
|
t.Fatalf("Expected zombies=0, got %v", count)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -31,7 +32,7 @@ import (
|
|||||||
"github.com/docker/docker/api/types/network"
|
"github.com/docker/docker/api/types/network"
|
||||||
"github.com/docker/docker/api/types/volume"
|
"github.com/docker/docker/api/types/volume"
|
||||||
"github.com/docker/docker/client"
|
"github.com/docker/docker/client"
|
||||||
"github.com/docker/go-connections/nat"
|
"github.com/docker/docker/pkg/stdcopy"
|
||||||
)
|
)
|
||||||
|
|
||||||
func imageName() string {
|
func imageName() string {
|
||||||
@@ -97,14 +98,14 @@ func runContainer(t *testing.T, cli *client.Client, containerConfig *container.C
|
|||||||
// if coverage
|
// if coverage
|
||||||
containerConfig.Env = append(containerConfig.Env, "COVERAGE_FILE="+t.Name()+".cov")
|
containerConfig.Env = append(containerConfig.Env, "COVERAGE_FILE="+t.Name()+".cov")
|
||||||
hostConfig := container.HostConfig{
|
hostConfig := container.HostConfig{
|
||||||
PortBindings: nat.PortMap{
|
// PortBindings: nat.PortMap{
|
||||||
"1414/tcp": []nat.PortBinding{
|
// "1414/tcp": []nat.PortBinding{
|
||||||
{
|
// {
|
||||||
HostIP: "0.0.0.0",
|
// HostIP: "0.0.0.0",
|
||||||
HostPort: "1414",
|
// HostPort: "1414",
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
Binds: []string{
|
Binds: []string{
|
||||||
coverageBind(t),
|
coverageBind(t),
|
||||||
},
|
},
|
||||||
@@ -141,11 +142,11 @@ func getCoverageExitCode(t *testing.T, orig int64) int64 {
|
|||||||
f := filepath.Join(coverageDir(t), "exitCode")
|
f := filepath.Join(coverageDir(t), "exitCode")
|
||||||
_, err := os.Stat(f)
|
_, err := os.Stat(f)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Log(err)
|
//t.Log(err)
|
||||||
return orig
|
return orig
|
||||||
}
|
}
|
||||||
// Remove the file, ready for the next test
|
// Remove the file, ready for the next test
|
||||||
//defer os.Remove(f)
|
defer os.Remove(f)
|
||||||
buf, err := ioutil.ReadFile(f)
|
buf, err := ioutil.ReadFile(f)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Log(err)
|
t.Log(err)
|
||||||
@@ -179,11 +180,45 @@ func waitForContainer(t *testing.T, cli *client.Client, ID string, timeout int64
|
|||||||
return rc
|
return rc
|
||||||
}
|
}
|
||||||
|
|
||||||
// execContainer runs the specified command inside the container, returning the
|
// execContainerWithExitCode runs a command in a running container, and returns the exit code
|
||||||
// exit code and the stdout/stderr string.
|
// Note: due to a bug in Docker/Moby code, you always get an exit code of 0 if you attach to the
|
||||||
func execContainer(t *testing.T, cli *client.Client, ID string, cmd []string) (int, string) {
|
// 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{
|
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,
|
Privileged: false,
|
||||||
Tty: false,
|
Tty: false,
|
||||||
AttachStdin: false,
|
AttachStdin: false,
|
||||||
@@ -207,46 +242,19 @@ func execContainer(t *testing.T, cli *client.Client, ID string, cmd []string) (i
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
inspect, err := cli.ContainerExecInspect(context.Background(), resp.ID)
|
buf := new(bytes.Buffer)
|
||||||
|
// Each output line has a header, which needs to be removed
|
||||||
|
_, err = stdcopy.StdCopy(buf, buf, hijack.Reader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
// TODO: For some reason, each line seems to start with an extra, random character
|
return strings.TrimSpace(buf.String())
|
||||||
buf, err := ioutil.ReadAll(hijack.Reader)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
hijack.Close()
|
|
||||||
return inspect.ExitCode, string(buf)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func waitForReady(t *testing.T, cli *client.Client, ID string) {
|
func waitForReady(t *testing.T, cli *client.Client, ID string) {
|
||||||
for {
|
for {
|
||||||
resp, err := cli.ContainerExecCreate(context.Background(), ID, types.ExecConfig{
|
rc := execContainerWithExitCode(t, cli, ID, "mqm", []string{"chkmqready"})
|
||||||
User: "mqm",
|
if rc == 0 {
|
||||||
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 {
|
|
||||||
t.Log("MQ is ready")
|
t.Log("MQ is ready")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -313,8 +321,11 @@ func inspectLogs(t *testing.T, cli *client.Client, ID string) string {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
buf.ReadFrom(reader)
|
// Each output line has a header, which needs to be removed
|
||||||
|
_, err = stdcopy.StdCopy(buf, buf, reader)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
return buf.String()
|
return buf.String()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user