Merge pull request #19 from arthurbarr/master
Miscellaneous improvements
This commit is contained in:
@@ -12,23 +12,23 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
# Build stage to build Go code
|
# Build stage to build Go code
|
||||||
|
###############################################################################
|
||||||
FROM golang:1.9 as builder
|
FROM golang:1.9 as builder
|
||||||
WORKDIR /go/src/github.com/ibm-messaging/mq-container/
|
WORKDIR /go/src/github.com/ibm-messaging/mq-container/
|
||||||
COPY cmd/ ./cmd
|
COPY cmd/ ./cmd
|
||||||
COPY pkg/ ./pkg
|
COPY internal/ ./internal
|
||||||
COPY vendor/ ./vendor
|
COPY vendor/ ./vendor
|
||||||
RUN go build ./cmd/runmqserver/
|
RUN go build ./cmd/runmqserver/
|
||||||
RUN go build ./cmd/chkmqready/
|
RUN go build ./cmd/chkmqready/
|
||||||
RUN go build ./cmd/chkmqhealthy/
|
RUN go build ./cmd/chkmqhealthy/
|
||||||
|
# Run all unit tests
|
||||||
|
RUN go test -v ./cmd/... ./internal/...
|
||||||
|
|
||||||
# Build stage to run Go unit tests
|
###############################################################################
|
||||||
FROM golang:1.9 as tester
|
|
||||||
COPY pkg/ ./pkg
|
|
||||||
RUN cd pkg/name && go test
|
|
||||||
RUN cd pkg/linux/capabilities && go test
|
|
||||||
|
|
||||||
# Main build stage, to build MQ image
|
# Main build stage, to build MQ image
|
||||||
|
###############################################################################
|
||||||
FROM ubuntu:16.04
|
FROM ubuntu:16.04
|
||||||
|
|
||||||
# The URL to download the MQ installer from in tar.gz format
|
# The URL to download the MQ installer from in tar.gz format
|
||||||
|
|||||||
@@ -16,9 +16,9 @@
|
|||||||
FROM golang:1.9 as builder
|
FROM golang:1.9 as builder
|
||||||
WORKDIR /go/src/github.com/ibm-messaging/mq-container/
|
WORKDIR /go/src/github.com/ibm-messaging/mq-container/
|
||||||
COPY cmd/ ./cmd
|
COPY cmd/ ./cmd
|
||||||
COPY pkg/ ./pkg
|
COPY internal/ ./internal
|
||||||
COPY vendor/ ./vendor
|
COPY vendor/ ./vendor
|
||||||
RUN go test -c -covermode=count ./cmd/runmqserver
|
RUN go test -c -covermode=count -coverpkg $(go list ./cmd/runmqserver ./internal/... | paste -s -d, -) ./cmd/runmqserver
|
||||||
|
|
||||||
FROM mq-advancedserver:latest-x86_64
|
FROM mq-advancedserver:latest-x86_64
|
||||||
|
|
||||||
|
|||||||
85
Makefile
85
Makefile
@@ -13,9 +13,7 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
BUILD_SERVER_CONTAINER=build-server
|
BUILD_SERVER_CONTAINER=build-server
|
||||||
# Set architecture for Go code. Don't set GOOS globally, so that tests can be run locally
|
DOCKER_TAG_ARCH ?= $(shell uname -m)
|
||||||
export GOARCH ?= amd64
|
|
||||||
DOCKER_TAG_ARCH ?= x86_64
|
|
||||||
# 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 run --tty --interactive --rm --volume /var/run/docker.sock:/var/run/docker.sock --volume "$(CURDIR)":/var/src --workdir /var/src docker:stable docker
|
||||||
@@ -24,7 +22,22 @@ DOCKER_REPO_DEVSERVER ?= mq-devserver
|
|||||||
DOCKER_REPO_ADVANCEDSERVER ?= mq-advancedserver
|
DOCKER_REPO_ADVANCEDSERVER ?= mq-advancedserver
|
||||||
DOCKER_FULL_DEVSERVER = $(DOCKER_REPO_DEVSERVER):$(DOCKER_TAG)
|
DOCKER_FULL_DEVSERVER = $(DOCKER_REPO_DEVSERVER):$(DOCKER_TAG)
|
||||||
DOCKER_FULL_ADVANCEDSERVER = $(DOCKER_REPO_ADVANCEDSERVER):$(DOCKER_TAG)
|
DOCKER_FULL_ADVANCEDSERVER = $(DOCKER_REPO_ADVANCEDSERVER):$(DOCKER_TAG)
|
||||||
|
# MQ_PACKAGES is the list of MQ packages to install
|
||||||
MQ_PACKAGES ?=ibmmq-server ibmmq-java ibmmq-jre ibmmq-gskit ibmmq-msg-.* ibmmq-samples ibmmq-ams
|
MQ_PACKAGES ?=ibmmq-server ibmmq-java ibmmq-jre ibmmq-gskit ibmmq-msg-.* ibmmq-samples ibmmq-ams
|
||||||
|
# MQ_VERSION is the fully qualified MQ version number to build
|
||||||
|
MQ_VERSION ?= 9.0.4.0
|
||||||
|
# Archive names for IBM MQ Continuous Delivery Release for Ubuntu
|
||||||
|
MQ_ARCHIVE_9.0.3.0_ppc64le=CNJR5ML.tar.gz
|
||||||
|
MQ_ARCHIVE_9.0.3.0_s390x=CNJR6ML.tar.gz
|
||||||
|
MQ_ARCHIVE_9.0.3.0_x86_64=CNJR7ML.tar.gz
|
||||||
|
MQ_ARCHIVE_9.0.4.0_ppc64le=CNLE2ML.tar.gz
|
||||||
|
MQ_ARCHIVE_9.0.4.0_s390x=CNLE3ML.tar.gz
|
||||||
|
MQ_ARCHIVE_9.0.4.0_x86_64=CNLE4ML.tar.gz
|
||||||
|
# Archive names for IBM MQ Advanced for Developers for Ubuntu
|
||||||
|
MQ_ARCHIVE_DEV_9.0.3.0=mqadv_dev903_ubuntu_x86-64.tar.gz
|
||||||
|
MQ_ARCHIVE_DEV_9.0.4.0=mqadv_dev904_ubuntu_x86-64.tar.gz
|
||||||
|
MQ_ARCHIVE ?= $(MQ_ARCHIVE_$(MQ_VERSION)_$(DOCKER_TAG_ARCH))
|
||||||
|
MQ_ARCHIVE_DEV=$(MQ_ARCHIVE_DEV_$(MQ_VERSION))
|
||||||
# Options to `go test` for the Docker tests
|
# Options to `go test` for the Docker tests
|
||||||
TEST_OPTS_DOCKER ?=
|
TEST_OPTS_DOCKER ?=
|
||||||
# Options to `go test` for the Kubernetes tests
|
# Options to `go test` for the Kubernetes tests
|
||||||
@@ -51,13 +64,13 @@ clean:
|
|||||||
rm -rf ./build
|
rm -rf ./build
|
||||||
rm -rf ./deps
|
rm -rf ./deps
|
||||||
|
|
||||||
downloads/mqadv_dev903_ubuntu_x86-64.tar.gz:
|
downloads/$(MQ_ARCHIVE_DEV):
|
||||||
$(info $(SPACER)$(shell printf $(TITLE)"Downloading IBM MQ Advanced for Developers"$(END)))
|
$(info $(SPACER)$(shell printf $(TITLE)"Downloading IBM MQ Advanced for Developers"$(END)))
|
||||||
mkdir -p downloads
|
mkdir -p downloads
|
||||||
cd downloads; curl -LO https://public.dhe.ibm.com/ibmdl/export/pub/software/websphere/messaging/mqadv/mqadv_dev903_ubuntu_x86-64.tar.gz
|
cd downloads; curl -LO https://public.dhe.ibm.com/ibmdl/export/pub/software/websphere/messaging/mqadv/$(MQ_ARCHIVE_DEV)
|
||||||
|
|
||||||
.PHONY: downloads
|
.PHONY: downloads
|
||||||
downloads: downloads/mqadv_dev903_ubuntu_x86-64.tar.gz
|
downloads: downloads/$(MQ_ARCHIVE_DEV)
|
||||||
|
|
||||||
.PHONY: deps
|
.PHONY: deps
|
||||||
deps:
|
deps:
|
||||||
@@ -72,18 +85,24 @@ build-cov:
|
|||||||
|
|
||||||
.PHONY: test-advancedserver
|
.PHONY: test-advancedserver
|
||||||
test-advancedserver:
|
test-advancedserver:
|
||||||
cd pkg/name && go test
|
$(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 $(TEST_OPTS_DOCKER)
|
||||||
|
|
||||||
.PHONY: test-devserver
|
.PHONY: test-devserver
|
||||||
test-devserver:
|
test-devserver:
|
||||||
$(info $(SPACER)$(shell printf $(TITLE)"Test $(DOCKER_FULL_DEVSERVER)"$(END)))
|
$(info $(SPACER)$(shell printf $(TITLE)"Test $(DOCKER_FULL_DEVSERVER) on Docker"$(END)))
|
||||||
cd pkg/name && go test
|
|
||||||
cd test/docker && TEST_IMAGE=$(DOCKER_FULL_DEVSERVER) go test
|
cd test/docker && TEST_IMAGE=$(DOCKER_FULL_DEVSERVER) go test
|
||||||
|
|
||||||
.PHONY: test-advancedserver-cover
|
.PHONY: test-advancedserver-cover
|
||||||
test-advancedserver-cover:
|
test-advancedserver-cover:
|
||||||
cd pkg/name && go test
|
$(info $(SPACER)$(shell printf $(TITLE)"Test $(DOCKER_REPO_ADVANCEDSERVER) on Docker with code coverage"$(END)))
|
||||||
|
rm -f ./coverage/unit*.cov
|
||||||
|
# Run unit tests with coverage, for each package under 'internal'
|
||||||
|
go list -f '{{.Name}}' ./internal/... | xargs -I {} go test -cover -covermode count -coverprofile ./coverage/unit-{}.cov ./internal/{}
|
||||||
|
echo 'mode: count' > ./coverage/unit.cov
|
||||||
|
tail -q -n +2 ./coverage/unit-*.cov >> ./coverage/unit.cov
|
||||||
|
go tool cover -html=./coverage/unit.cov -o ./coverage/unit.html
|
||||||
|
|
||||||
rm -f ./test/docker/coverage/*.cov
|
rm -f ./test/docker/coverage/*.cov
|
||||||
rm -f ./coverage/docker.*
|
rm -f ./coverage/docker.*
|
||||||
cd test/docker && TEST_IMAGE=$(DOCKER_REPO_ADVANCEDSERVER):cover go test $(TEST_OPTS_DOCKER)
|
cd test/docker && TEST_IMAGE=$(DOCKER_REPO_ADVANCEDSERVER):cover go test $(TEST_OPTS_DOCKER)
|
||||||
@@ -91,6 +110,10 @@ test-advancedserver-cover:
|
|||||||
tail -q -n +2 ./test/docker/coverage/*.cov >> ./coverage/docker.cov
|
tail -q -n +2 ./test/docker/coverage/*.cov >> ./coverage/docker.cov
|
||||||
go tool cover -html=./coverage/docker.cov -o ./coverage/docker.html
|
go tool cover -html=./coverage/docker.cov -o ./coverage/docker.html
|
||||||
|
|
||||||
|
echo 'mode: count' > ./coverage/combined.cov
|
||||||
|
tail -q -n +2 ./coverage/unit.cov ./coverage/docker.cov >> ./coverage/combined.cov
|
||||||
|
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:
|
||||||
$(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")
|
||||||
@@ -127,38 +150,24 @@ define docker-build-mq
|
|||||||
--label IBM_PRODUCT_NAME=$5 \
|
--label IBM_PRODUCT_NAME=$5 \
|
||||||
--label IBM_PRODUCT_VERSION=$6 \
|
--label IBM_PRODUCT_VERSION=$6 \
|
||||||
--build-arg MQ_PACKAGES="$(MQ_PACKAGES)" \
|
--build-arg MQ_PACKAGES="$(MQ_PACKAGES)" \
|
||||||
.
|
. ; $(DOCKER) kill $(BUILD_SERVER_CONTAINER) && $(DOCKER) network rm build
|
||||||
# Stop the web server (will also remove the container)
|
|
||||||
$(DOCKER) kill $(BUILD_SERVER_CONTAINER)
|
|
||||||
# Delete the temporary network
|
|
||||||
$(DOCKER) network rm build
|
|
||||||
endef
|
endef
|
||||||
|
|
||||||
# .PHONY: build-advancedserver-903
|
|
||||||
# build-advancedserver-903: build downloads/CNJR7ML.tar.gz
|
|
||||||
# $(info $(SPACER)$(shell printf $(TITLE)"Build $(DOCKER_FULL_ADVANCEDSERVER)"$(END)))
|
|
||||||
# $(call docker-build-mq,$(DOCKER_FULL_ADVANCEDSERVER),Dockerfile-server,CNJR7ML.tar.gz,"4486e8c4cc9146fd9b3ce1f14a2dfc5b","IBM MQ Advanced","9.0.3")
|
|
||||||
# $(DOCKER) tag $(DOCKER_FULL_ADVANCEDSERVER) $(DOCKER_REPO_ADVANCEDSERVER):9.0.3-$(DOCKER_TAG_ARCH)
|
|
||||||
|
|
||||||
.PHONY: build-advancedserver-904
|
|
||||||
build-advancedserver-904: downloads/CNLE4ML.tar.gz
|
|
||||||
$(info $(SPACER)$(shell printf $(TITLE)"Build $(DOCKER_FULL_ADVANCEDSERVER)"$(END)))
|
|
||||||
$(call docker-build-mq,$(DOCKER_FULL_ADVANCEDSERVER),Dockerfile-server,CNLE4ML.tar.gz,"4486e8c4cc9146fd9b3ce1f14a2dfc5b","IBM MQ Advanced","9.0.4")
|
|
||||||
$(DOCKER) tag $(DOCKER_FULL_ADVANCEDSERVER) $(DOCKER_REPO_ADVANCEDSERVER):9.0.4-$(DOCKER_TAG_ARCH)
|
|
||||||
|
|
||||||
.PHONY: build-advancedserver
|
.PHONY: build-advancedserver
|
||||||
build-advancedserver: build-advancedserver-904
|
build-advancedserver: downloads/$(MQ_ARCHIVE)
|
||||||
|
$(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))
|
||||||
|
$(DOCKER) tag $(DOCKER_FULL_ADVANCEDSERVER) $(DOCKER_REPO_ADVANCEDSERVER):$(MQ_VERSION)-$(DOCKER_TAG_ARCH)
|
||||||
|
|
||||||
.PHONY: build-devserver
|
.PHONY: build-devserver
|
||||||
build-devserver: downloads/mqadv_dev903_ubuntu_x86-64.tar.gz
|
build-devserver: downloads/$(MQ_ARCHIVE_DEV)
|
||||||
|
ifneq "x86_64" "$(shell uname -m)"
|
||||||
|
$(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,mqadv_dev903_ubuntu_x86-64.tar.gz,"98102d16795c4263ad9ca075190a2d4d","IBM MQ Advanced for Developers (Non-Warranted)","9.0.3")
|
$(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):9.0.3-$(DOCKER_TAG_ARCH)
|
$(DOCKER) tag $(DOCKER_FULL_DEVSERVER) $(DOCKER_REPO_DEVSERVER):$(MQ_VERSION)-$(DOCKER_TAG_ARCH)
|
||||||
|
endif
|
||||||
# .PHONY: build-server
|
|
||||||
# build-server: build downloads/CNJR7ML.tar.gz
|
|
||||||
# $(call docker-build-mq,mq-server:latest-$(DOCKER_TAG_ARCH),Dockerfile-server,"79afd716d55b4f149a87bec52c9dc1aa","IBM MQ","9.0.3")
|
|
||||||
# $(DOCKER) tag mq-server:latest-$(DOCKER_TAG_ARCH) mq-server:9.0.3-$(DOCKER_TAG_ARCH)
|
|
||||||
|
|
||||||
.PHONY: build-advancedserver-cover
|
.PHONY: build-advancedserver-cover
|
||||||
build-advancedserver-cover:
|
build-advancedserver-cover:
|
||||||
@@ -169,7 +178,7 @@ build-advancedserver-cover:
|
|||||||
# $(call docker-build-mq,mq-web:latest-$(DOCKER_TAG_ARCH),Dockerfile-mq-web)
|
# $(call docker-build-mq,mq-web:latest-$(DOCKER_TAG_ARCH),Dockerfile-mq-web)
|
||||||
|
|
||||||
.PHONY: build-explorer
|
.PHONY: build-explorer
|
||||||
build-explorer: downloads/mqadv_dev903_ubuntu_x86-64.tar.gz
|
build-explorer: downloads/$(MQ_ARCHIVE_DEV)
|
||||||
$(call docker-build-mq,mq-explorer:latest-$(DOCKER_TAG_ARCH),incubating/mq-explorer/Dockerfile-mq-explorer,mqadv_dev903_ubuntu_x86-64.tar.gz,"98102d16795c4263ad9ca075190a2d4d","IBM MQ Advanced for Developers (Non-Warranted)","9.0.3")
|
$(call docker-build-mq,mq-explorer:latest-$(DOCKER_TAG_ARCH),incubating/mq-explorer/Dockerfile-mq-explorer,$(MQ_ARCHIVE_DEV),"98102d16795c4263ad9ca075190a2d4d","IBM MQ Advanced for Developers (Non-Warranted)",$(MQ_VERSION))
|
||||||
|
|
||||||
include formatting.mk
|
include formatting.mk
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import (
|
|||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/ibm-messaging/mq-container/pkg/name"
|
"github.com/ibm-messaging/mq-container/internal/name"
|
||||||
)
|
)
|
||||||
|
|
||||||
func queueManagerHealthy() (bool, error) {
|
func queueManagerHealthy() (bool, error) {
|
||||||
|
|||||||
91
cmd/runmqserver/license.go
Normal file
91
cmd/runmqserver/license.go
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
© 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 (
|
||||||
|
"errors"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// resolveLicenseFile returns the file name of the MQ license file, taking into
|
||||||
|
// account the language set by the LANG environment variable
|
||||||
|
func resolveLicenseFile() string {
|
||||||
|
lang, ok := os.LookupEnv("LANG")
|
||||||
|
if !ok {
|
||||||
|
return "English.txt"
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case strings.HasPrefix(lang, "zh_TW"):
|
||||||
|
return "Chinese_TW.txt"
|
||||||
|
case strings.HasPrefix(lang, "zh"):
|
||||||
|
return "Chinese.txt"
|
||||||
|
case strings.HasPrefix(lang, "cs"):
|
||||||
|
return "Czech.txt"
|
||||||
|
case strings.HasPrefix(lang, "fr"):
|
||||||
|
return "French.txt"
|
||||||
|
case strings.HasPrefix(lang, "de"):
|
||||||
|
return "German.txt"
|
||||||
|
case strings.HasPrefix(lang, "el"):
|
||||||
|
return "Greek.txt"
|
||||||
|
case strings.HasPrefix(lang, "id"):
|
||||||
|
return "Indonesian.txt"
|
||||||
|
case strings.HasPrefix(lang, "it"):
|
||||||
|
return "Italian.txt"
|
||||||
|
case strings.HasPrefix(lang, "ja"):
|
||||||
|
return "Japanese.txt"
|
||||||
|
case strings.HasPrefix(lang, "ko"):
|
||||||
|
return "Korean.txt"
|
||||||
|
case strings.HasPrefix(lang, "lt"):
|
||||||
|
return "Lithuanian.txt"
|
||||||
|
case strings.HasPrefix(lang, "pl"):
|
||||||
|
return "Polish.txt"
|
||||||
|
case strings.HasPrefix(lang, "pt"):
|
||||||
|
return "Portugese.txt"
|
||||||
|
case strings.HasPrefix(lang, "ru"):
|
||||||
|
return "Russian.txt"
|
||||||
|
case strings.HasPrefix(lang, "sl"):
|
||||||
|
return "Slovenian.txt"
|
||||||
|
case strings.HasPrefix(lang, "es"):
|
||||||
|
return "Spanish.txt"
|
||||||
|
case strings.HasPrefix(lang, "tr"):
|
||||||
|
return "Turkish.txt"
|
||||||
|
}
|
||||||
|
return "English.txt"
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkLicense() (bool, error) {
|
||||||
|
lic, ok := os.LookupEnv("LICENSE")
|
||||||
|
switch {
|
||||||
|
case ok && lic == "accept":
|
||||||
|
return true, nil
|
||||||
|
case ok && lic == "view":
|
||||||
|
file := filepath.Join("/opt/mqm/licenses", resolveLicenseFile())
|
||||||
|
buf, err := ioutil.ReadFile(file)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
log.Println(string(buf))
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
log.Println("Error: Set environment variable LICENSE=accept to indicate acceptance of license terms and conditions.")
|
||||||
|
log.Println("License agreements and information can be viewed by setting the environment variable LICENSE=view. You can also set the LANG environment variable to view the license in a different language.")
|
||||||
|
return false, errors.New("Set environment variable LICENSE=accept to indicate acceptance of license terms and conditions")
|
||||||
|
}
|
||||||
@@ -18,182 +18,73 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"errors"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"os/signal"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
|
||||||
"runtime"
|
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
"github.com/ibm-messaging/mq-container/internal/command"
|
||||||
|
"github.com/ibm-messaging/mq-container/internal/name"
|
||||||
)
|
)
|
||||||
|
|
||||||
// resolveLicenseFile returns the file name of the MQ license file, taking into
|
|
||||||
// account the language set by the LANG environment variable
|
|
||||||
func resolveLicenseFile() string {
|
|
||||||
lang, ok := os.LookupEnv("LANG")
|
|
||||||
if !ok {
|
|
||||||
return "English.txt"
|
|
||||||
}
|
|
||||||
switch {
|
|
||||||
case strings.HasPrefix(lang, "zh_TW"):
|
|
||||||
return "Chinese_TW.txt"
|
|
||||||
case strings.HasPrefix(lang, "zh"):
|
|
||||||
return "Chinese.txt"
|
|
||||||
case strings.HasPrefix(lang, "cs"):
|
|
||||||
return "Czech.txt"
|
|
||||||
case strings.HasPrefix(lang, "fr"):
|
|
||||||
return "French.txt"
|
|
||||||
case strings.HasPrefix(lang, "de"):
|
|
||||||
return "German.txt"
|
|
||||||
case strings.HasPrefix(lang, "el"):
|
|
||||||
return "Greek.txt"
|
|
||||||
case strings.HasPrefix(lang, "id"):
|
|
||||||
return "Indonesian.txt"
|
|
||||||
case strings.HasPrefix(lang, "it"):
|
|
||||||
return "Italian.txt"
|
|
||||||
case strings.HasPrefix(lang, "ja"):
|
|
||||||
return "Japanese.txt"
|
|
||||||
case strings.HasPrefix(lang, "ko"):
|
|
||||||
return "Korean.txt"
|
|
||||||
case strings.HasPrefix(lang, "lt"):
|
|
||||||
return "Lithuanian.txt"
|
|
||||||
case strings.HasPrefix(lang, "pl"):
|
|
||||||
return "Polish.txt"
|
|
||||||
case strings.HasPrefix(lang, "pt"):
|
|
||||||
return "Portugese.txt"
|
|
||||||
case strings.HasPrefix(lang, "ru"):
|
|
||||||
return "Russian.txt"
|
|
||||||
case strings.HasPrefix(lang, "sl"):
|
|
||||||
return "Slovenian.txt"
|
|
||||||
case strings.HasPrefix(lang, "es"):
|
|
||||||
return "Spanish.txt"
|
|
||||||
case strings.HasPrefix(lang, "tr"):
|
|
||||||
return "Turkish.txt"
|
|
||||||
}
|
|
||||||
return "English.txt"
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkLicense() {
|
|
||||||
lic, ok := os.LookupEnv("LICENSE")
|
|
||||||
switch {
|
|
||||||
case ok && lic == "accept":
|
|
||||||
return
|
|
||||||
case ok && lic == "view":
|
|
||||||
file := filepath.Join("/opt/mqm/licenses", resolveLicenseFile())
|
|
||||||
buf, err := ioutil.ReadFile(file)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
fmt.Println(string(buf))
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
fmt.Println("Error: Set environment variable LICENSE=accept to indicate acceptance of license terms and conditions.")
|
|
||||||
fmt.Println("License agreements and information can be viewed by setting the environment variable LICENSE=view. You can also set the LANG environment variable to view the license in a different language.")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// sanitizeQueueManagerName removes any invalid characters from a queue manager name
|
|
||||||
func sanitizeQueueManagerName(name string) string {
|
|
||||||
var re = regexp.MustCompile("[^a-zA-Z0-9._%/]")
|
|
||||||
return re.ReplaceAllString(name, "")
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetQueueManagerName resolves the queue manager name to use. Resolved from
|
|
||||||
// either an environment variable, or the hostname.
|
|
||||||
func getQueueManagerName() (string, error) {
|
|
||||||
var name string
|
|
||||||
var err error
|
|
||||||
name, ok := os.LookupEnv("MQ_QMGR_NAME")
|
|
||||||
if !ok || name == "" {
|
|
||||||
name, err = os.Hostname()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
name = sanitizeQueueManagerName(name)
|
|
||||||
}
|
|
||||||
// TODO: What if the specified env variable is an invalid name?
|
|
||||||
return name, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// runCommand runs an OS command. On Linux it waits for the command to
|
|
||||||
// complete and returns the exit status (return code).
|
|
||||||
func runCommand(name string, arg ...string) (string, int, error) {
|
|
||||||
cmd := exec.Command(name, arg...)
|
|
||||||
// Run the command and wait for completion
|
|
||||||
out, err := cmd.CombinedOutput()
|
|
||||||
if err != nil {
|
|
||||||
var rc int
|
|
||||||
// Only works on Linux
|
|
||||||
if runtime.GOOS == "linux" {
|
|
||||||
var ws unix.WaitStatus
|
|
||||||
unix.Wait4(cmd.Process.Pid, &ws, 0, nil)
|
|
||||||
rc = ws.ExitStatus()
|
|
||||||
} else {
|
|
||||||
rc = -1
|
|
||||||
}
|
|
||||||
if rc == 0 {
|
|
||||||
return string(out), rc, nil
|
|
||||||
}
|
|
||||||
return string(out), rc, err
|
|
||||||
}
|
|
||||||
return string(out), 0, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// createDirStructure creates the default MQ directory structure under /var/mqm
|
// createDirStructure creates the default MQ directory structure under /var/mqm
|
||||||
func createDirStructure() {
|
func createDirStructure() error {
|
||||||
out, _, err := runCommand("/opt/mqm/bin/crtmqdir", "-f", "-s")
|
out, _, err := command.Run("/opt/mqm/bin/crtmqdir", "-f", "-s")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Error creating directory structure: %v\n", string(out))
|
log.Printf("Error creating directory structure: %v\n", string(out))
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
log.Println("Created directory structure under /var/mqm")
|
log.Println("Created directory structure under /var/mqm")
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func createQueueManager(name string) {
|
func createQueueManager(name string) error {
|
||||||
log.Printf("Creating queue manager %v", name)
|
log.Printf("Creating queue manager %v", name)
|
||||||
out, rc, err := runCommand("crtmqm", "-q", "-p", "1414", name)
|
out, rc, err := command.Run("crtmqm", "-q", "-p", "1414", name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// 8=Queue manager exists, which is fine
|
// 8=Queue manager exists, which is fine
|
||||||
if rc != 8 {
|
if rc != 8 {
|
||||||
log.Printf("crtmqm returned %v", rc)
|
log.Printf("crtmqm returned %v", rc)
|
||||||
log.Fatalln(string(out))
|
log.Println(string(out))
|
||||||
} else {
|
return err
|
||||||
log.Printf("Detected existing queue manager %v", name)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
log.Printf("Detected existing queue manager %v", name)
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateCommandLevel() {
|
func updateCommandLevel() error {
|
||||||
level, ok := os.LookupEnv("MQ_CMDLEVEL")
|
level, ok := os.LookupEnv("MQ_CMDLEVEL")
|
||||||
if ok && level != "" {
|
if ok && level != "" {
|
||||||
out, rc, err := runCommand("strmqm", "-e", "CMDLEVEL="+level)
|
out, rc, err := command.Run("strmqm", "-e", "CMDLEVEL="+level)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Error %v setting CMDLEVEL: %v", rc, string(out))
|
log.Printf("Error %v setting CMDLEVEL: %v", rc, string(out))
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func startQueueManager() {
|
func startQueueManager() error {
|
||||||
log.Println("Starting queue manager")
|
log.Println("Starting queue manager")
|
||||||
out, rc, err := runCommand("strmqm")
|
out, rc, err := command.Run("strmqm")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Error %v starting queue manager: %v", rc, string(out))
|
log.Printf("Error %v starting queue manager: %v", rc, string(out))
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
log.Println("Started queue manager")
|
log.Println("Started queue manager")
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func configureQueueManager() {
|
func configureQueueManager() error {
|
||||||
const configDir string = "/etc/mqm"
|
const configDir string = "/etc/mqm"
|
||||||
files, err := ioutil.ReadDir(configDir)
|
files, err := ioutil.ReadDir(configDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Println(err)
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, file := range files {
|
for _, file := range files {
|
||||||
@@ -201,12 +92,14 @@ func configureQueueManager() {
|
|||||||
abs := filepath.Join(configDir, file.Name())
|
abs := filepath.Join(configDir, file.Name())
|
||||||
mqsc, err := ioutil.ReadFile(abs)
|
mqsc, err := ioutil.ReadFile(abs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Println(err)
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
cmd := exec.Command("runmqsc")
|
cmd := exec.Command("runmqsc")
|
||||||
stdin, err := cmd.StdinPipe()
|
stdin, err := cmd.StdinPipe()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Println(err)
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
stdin.Write(mqsc)
|
stdin.Write(mqsc)
|
||||||
stdin.Close()
|
stdin.Close()
|
||||||
@@ -219,78 +112,78 @@ func configureQueueManager() {
|
|||||||
log.Printf("Output for \"runmqsc\" with %v:\n\t%v", abs, strings.Replace(string(out), "\n", "\n\t", -1))
|
log.Printf("Output for \"runmqsc\" with %v:\n\t%v", abs, strings.Replace(string(out), "\n", "\n\t", -1))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func stopQueueManager() {
|
func stopQueueManager(name string) error {
|
||||||
log.Println("Stopping queue manager")
|
log.Println("Stopping queue manager")
|
||||||
out, _, err := runCommand("endmqm", "-w")
|
out, _, err := command.Run("endmqm", "-w", name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Error stopping queue manager: %v", string(out))
|
log.Printf("Error stopping queue manager: %v", string(out))
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
log.Println("Stopped queue manager")
|
log.Println("Stopped queue manager")
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// createTerminateChannel creates a channel which will be closed when SIGTERM
|
func doMain() error {
|
||||||
// is received.
|
accepted, err := checkLicense()
|
||||||
func createTerminateChannel() chan struct{} {
|
|
||||||
done := make(chan struct{})
|
|
||||||
// Handle SIGTERM
|
|
||||||
c := make(chan os.Signal, 1)
|
|
||||||
signal.Notify(c, syscall.SIGTERM, syscall.SIGINT)
|
|
||||||
go func() {
|
|
||||||
sig := <-c
|
|
||||||
log.Printf("Signal received: %v", sig)
|
|
||||||
stopQueueManager()
|
|
||||||
close(done)
|
|
||||||
}()
|
|
||||||
return done
|
|
||||||
}
|
|
||||||
|
|
||||||
// createReaperChannel creates a channel which will be used to reap zombie
|
|
||||||
// (defunct) processes. This is a responsibility of processes running
|
|
||||||
// as PID 1.
|
|
||||||
func createReaper() {
|
|
||||||
// Handle SIGCHLD
|
|
||||||
c := make(chan os.Signal, 3)
|
|
||||||
signal.Notify(c, syscall.SIGCHLD)
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
<-c
|
|
||||||
for {
|
|
||||||
var ws unix.WaitStatus
|
|
||||||
_, err := unix.Wait4(-1, &ws, 0, nil)
|
|
||||||
// If err indicates "no child processes" left to reap, then
|
|
||||||
// wait for next SIGCHLD signal
|
|
||||||
if err == unix.ECHILD {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
createReaper()
|
|
||||||
checkLicense()
|
|
||||||
// Start SIGTERM handler channel
|
|
||||||
done := createTerminateChannel()
|
|
||||||
|
|
||||||
name, err := getQueueManagerName()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalln(err)
|
return err
|
||||||
|
}
|
||||||
|
if !accepted {
|
||||||
|
return errors.New("License not accepted")
|
||||||
|
}
|
||||||
|
|
||||||
|
name, err := name.GetQueueManagerName()
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
log.Printf("Using queue manager name: %v", name)
|
log.Printf("Using queue manager name: %v", name)
|
||||||
|
|
||||||
|
// Start signal handler
|
||||||
|
signalControl := signalHandler(name)
|
||||||
|
|
||||||
logConfig()
|
logConfig()
|
||||||
err = createVolume("/mnt/mqm")
|
err = createVolume("/mnt/mqm")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Println(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = createDirStructure()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = createQueueManager(name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = updateCommandLevel()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = startQueueManager()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
createDirStructure()
|
|
||||||
createQueueManager(name)
|
|
||||||
updateCommandLevel()
|
|
||||||
startQueueManager()
|
|
||||||
configureQueueManager()
|
configureQueueManager()
|
||||||
|
// Start reaping zombies from now on.
|
||||||
|
// Start this here, so that we don't reap any sub-processes created
|
||||||
|
// by this process (e.g. for crtmqm or strmqm)
|
||||||
|
signalControl <- startReaping
|
||||||
|
// Reap zombies now, just in case we've already got some
|
||||||
|
signalControl <- reapNow
|
||||||
// Wait for terminate signal
|
// Wait for terminate signal
|
||||||
<-done
|
<-signalControl
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var osExit = os.Exit
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
err := doMain()
|
||||||
|
if err != nil {
|
||||||
|
osExit(1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,9 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -29,6 +32,15 @@ func init() {
|
|||||||
// Test started when the test binary is started. Only calls main.
|
// Test started when the test binary is started. Only calls main.
|
||||||
func TestSystem(t *testing.T) {
|
func TestSystem(t *testing.T) {
|
||||||
if *test {
|
if *test {
|
||||||
|
var oldExit = osExit
|
||||||
|
defer func() {
|
||||||
|
osExit = oldExit
|
||||||
|
}()
|
||||||
|
osExit = func(rc int) {
|
||||||
|
// Write the exit code to a file instead
|
||||||
|
log.Printf("Writing exit code %v to file", strconv.Itoa(rc))
|
||||||
|
ioutil.WriteFile("/var/coverage/exitCode", []byte(strconv.Itoa(rc)), 0644)
|
||||||
|
}
|
||||||
main()
|
main()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/ibm-messaging/mq-container/pkg/linux/capabilities"
|
"github.com/ibm-messaging/mq-container/internal/capabilities"
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
79
cmd/runmqserver/signals.go
Normal file
79
cmd/runmqserver/signals.go
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
/*
|
||||||
|
© 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 (
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
startReaping = iota
|
||||||
|
reapNow = iota
|
||||||
|
)
|
||||||
|
|
||||||
|
func signalHandler(qmgr string) chan int {
|
||||||
|
control := make(chan int)
|
||||||
|
// Use separate channels for the signals, to avoid SIGCHLD signals swamping
|
||||||
|
// the buffer, and preventing other signals.
|
||||||
|
stopSignals := make(chan os.Signal)
|
||||||
|
reapSignals := make(chan os.Signal)
|
||||||
|
signal.Notify(stopSignals, syscall.SIGTERM, syscall.SIGINT)
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case sig := <-stopSignals:
|
||||||
|
log.Printf("Signal received: %v", sig)
|
||||||
|
signal.Stop(reapSignals)
|
||||||
|
signal.Stop(stopSignals)
|
||||||
|
stopQueueManager(qmgr)
|
||||||
|
// One final reap
|
||||||
|
reapZombies()
|
||||||
|
close(control)
|
||||||
|
// End the goroutine
|
||||||
|
return
|
||||||
|
case <-reapSignals:
|
||||||
|
reapZombies()
|
||||||
|
case job := <-control:
|
||||||
|
switch {
|
||||||
|
case job == startReaping:
|
||||||
|
// Add SIGCHLD to the list of signals we're listening to
|
||||||
|
signal.Notify(reapSignals, syscall.SIGCHLD)
|
||||||
|
case job == reapNow:
|
||||||
|
reapZombies()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return control
|
||||||
|
}
|
||||||
|
|
||||||
|
// reapZombies reaps any zombie (terminated) processes now.
|
||||||
|
// This function should be called before exiting.
|
||||||
|
func reapZombies() {
|
||||||
|
for {
|
||||||
|
var ws unix.WaitStatus
|
||||||
|
pid, err := unix.Wait4(-1, &ws, unix.WNOHANG, nil)
|
||||||
|
// If err or pid indicate "no child processes"
|
||||||
|
if pid == 0 || err == unix.ECHILD {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,18 +8,37 @@ You need to ensure you have the following tools installed:
|
|||||||
* [Glide](https://glide.sh/)
|
* [Glide](https://glide.sh/)
|
||||||
* [dep](https://github.com/golang/dep) (official Go dependency management tool)
|
* [dep](https://github.com/golang/dep) (official Go dependency management tool)
|
||||||
* make
|
* make
|
||||||
|
* [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/).
|
||||||
|
|
||||||
|
## Building a production image
|
||||||
|
This procedure works for building the MQ Continuous Delivery release, on `x86_64`, `ppc64le` and `s390x` architectures.
|
||||||
|
|
||||||
|
1. Download MQ from IBM Passport Advantage, and place the downloaded file (for example, `CNLE4ML.tar.gz` for MQ V9.0.4 on x86_64 architecture) in the `downloads` directory
|
||||||
|
2. Run `make build-advancedserver`
|
||||||
|
|
||||||
|
You can build a different version of MQ by setting the `MQ_VERSION` environment variable, for example:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
MQ_VERSION=9.0.3.0 make build-advancedserver
|
||||||
|
```
|
||||||
|
|
||||||
## Running the tests
|
## Running the tests
|
||||||
There are three main sets of tests:
|
There are three main sets of tests:
|
||||||
|
|
||||||
1. Unit tests
|
1. Unit tests, which are run during a build
|
||||||
2. Docker tests, which test a complete Docker image, using the Docker API
|
2. Docker tests, which test a complete Docker image, using the Docker API
|
||||||
3. Kubernetes tests, which test the Helm charts (and the Docker image) via [Helm](https://helm.sh)
|
3. Kubernetes tests, which test the Helm charts (and the Docker image) via [Helm](https://helm.sh)
|
||||||
|
|
||||||
### Running the tests
|
### Running the Docker tests
|
||||||
The unit and Docker tests can be run locally. For example:
|
The Docker tests can be run locally. Before you run them for the first time, you need to download the test dependencies:
|
||||||
|
|
||||||
|
```
|
||||||
|
make deps
|
||||||
|
```
|
||||||
|
|
||||||
|
You can then run the tests, for example:
|
||||||
|
|
||||||
```
|
```
|
||||||
make test-devserver
|
make test-devserver
|
||||||
@@ -31,7 +50,7 @@ or:
|
|||||||
make test-advancedserver
|
make test-advancedserver
|
||||||
```
|
```
|
||||||
|
|
||||||
### Running the 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:
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|||||||
47
internal/command/command.go
Normal file
47
internal/command/command.go
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
© 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 command contains code to run external commands
|
||||||
|
package command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os/exec"
|
||||||
|
"runtime"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Run runs an OS command. On Linux it waits for the command to
|
||||||
|
// complete and returns the exit status (return code).
|
||||||
|
// Do not use this function to run shell built-ins (like "cd"), because
|
||||||
|
// the error handling works differently
|
||||||
|
func Run(name string, arg ...string) (string, int, error) {
|
||||||
|
cmd := exec.Command(name, arg...)
|
||||||
|
// Run the command and wait for completion
|
||||||
|
out, err := cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
// Assert that this is an ExitError
|
||||||
|
exiterr, ok := err.(*exec.ExitError)
|
||||||
|
// If the type assertion was correct, and we're on Linux
|
||||||
|
if ok && runtime.GOOS == "linux" {
|
||||||
|
status, ok := exiterr.Sys().(syscall.WaitStatus)
|
||||||
|
if ok {
|
||||||
|
return string(out), status.ExitStatus(), err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return string(out), -1, err
|
||||||
|
}
|
||||||
|
return string(out), 0, nil
|
||||||
|
}
|
||||||
47
internal/command/command_test.go
Normal file
47
internal/command/command_test.go
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
© 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 command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"runtime"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var commandTests = []struct {
|
||||||
|
name string
|
||||||
|
arg []string
|
||||||
|
rc int
|
||||||
|
}{
|
||||||
|
{"ls", []string{}, 0},
|
||||||
|
{"ls", []string{"madeup"}, 2},
|
||||||
|
{"bash", []string{"-c", "exit 99"}, 99},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRun(t *testing.T) {
|
||||||
|
if runtime.GOOS != "linux" {
|
||||||
|
t.Skip("Skipping tests for package which only works on Linux")
|
||||||
|
}
|
||||||
|
for _, table := range commandTests {
|
||||||
|
arg := table.arg
|
||||||
|
_, rc, err := Run(table.name, arg...)
|
||||||
|
if rc != table.rc {
|
||||||
|
t.Errorf("Run(%v,%v) - expected %v, got %v", table.name, table.arg, table.rc, rc)
|
||||||
|
}
|
||||||
|
if rc != 0 && err == nil {
|
||||||
|
t.Errorf("Run(%v,%v) - expected error for non-zero return code (rc=%v)", table.name, table.arg, rc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,11 +20,9 @@ package name
|
|||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
//log "github.com/sirupsen/logrus"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// sanitizeQueueManagerName removes any invalid characters from a queue manager name
|
// sanitizeQueueManagerName removes any invalid characters from a queue manager name
|
||||||
// TODO: This is duplicate code
|
|
||||||
func sanitizeQueueManagerName(name string) string {
|
func sanitizeQueueManagerName(name string) string {
|
||||||
var re = regexp.MustCompile("[^a-zA-Z0-9._%/]")
|
var re = regexp.MustCompile("[^a-zA-Z0-9._%/]")
|
||||||
return re.ReplaceAllString(name, "")
|
return re.ReplaceAllString(name, "")
|
||||||
@@ -167,3 +167,55 @@ func TestNoVolumeWithRestart(t *testing.T) {
|
|||||||
startContainer(t, cli, id)
|
startContainer(t, cli, id)
|
||||||
waitForReady(t, cli, id)
|
waitForReady(t, cli, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test the case where `crtmqm` will fail
|
||||||
|
func TestCreateQueueManagerFail(t *testing.T) {
|
||||||
|
cli, err := client.NewEnvClient()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
img, _, err := cli.ImageInspectWithRaw(context.Background(), imageName())
|
||||||
|
oldEntrypoint := strings.Join(img.Config.Entrypoint, " ")
|
||||||
|
containerConfig := container.Config{
|
||||||
|
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.
|
||||||
|
// 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},
|
||||||
|
}
|
||||||
|
id := runContainer(t, cli, &containerConfig)
|
||||||
|
defer cleanContainer(t, cli, id)
|
||||||
|
rc := waitForContainer(t, cli, id, 10)
|
||||||
|
if rc != 1 {
|
||||||
|
t.Errorf("Expected rc=1, got rc=%v", rc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test the case where `strmqm` will fail
|
||||||
|
func TestStartQueueManagerFail(t *testing.T) {
|
||||||
|
cli, err := client.NewEnvClient()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
img, _, err := cli.ImageInspectWithRaw(context.Background(), imageName())
|
||||||
|
oldEntrypoint := strings.Join(img.Config.Entrypoint, " ")
|
||||||
|
containerConfig := container.Config{
|
||||||
|
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.
|
||||||
|
// 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},
|
||||||
|
}
|
||||||
|
id := runContainer(t, cli, &containerConfig)
|
||||||
|
defer cleanContainer(t, cli, id)
|
||||||
|
rc := waitForContainer(t, cli, id, 10)
|
||||||
|
if rc != 1 {
|
||||||
|
t.Errorf("Expected rc=1, got rc=%v", rc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -61,13 +62,15 @@ func cleanContainer(t *testing.T, cli *client.Client, ID string) {
|
|||||||
// Log the results and continue
|
// Log the results and continue
|
||||||
t.Logf("Inspected container %v: %#v", ID, i)
|
t.Logf("Inspected container %v: %#v", ID, i)
|
||||||
}
|
}
|
||||||
t.Logf("Killing container: %v", ID)
|
t.Logf("Stopping container: %v", ID)
|
||||||
// Kill the container. This allows the coverage output to be generated.
|
timeout := 10 * time.Second
|
||||||
err = cli.ContainerKill(context.Background(), ID, "SIGTERM")
|
// Stop the container. This allows the coverage output to be generated.
|
||||||
|
err = cli.ContainerStop(context.Background(), ID, &timeout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Just log the error and continue
|
// Just log the error and continue
|
||||||
t.Log(err)
|
t.Log(err)
|
||||||
}
|
}
|
||||||
|
t.Log("Container stopped")
|
||||||
// If a code coverage file has been generated, then rename it to match the test name
|
// If a code coverage file has been generated, then rename it to match the test name
|
||||||
os.Rename(filepath.Join(coverageDir(t), "container.cov"), filepath.Join(coverageDir(t), t.Name()+".cov"))
|
os.Rename(filepath.Join(coverageDir(t), "container.cov"), filepath.Join(coverageDir(t), t.Name()+".cov"))
|
||||||
// Log the container output for any container we're about to delete
|
// Log the container output for any container we're about to delete
|
||||||
@@ -134,11 +137,40 @@ func stopContainer(t *testing.T, cli *client.Client, ID string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getCoverageExitCode(t *testing.T, orig int64) int64 {
|
||||||
|
f := filepath.Join(coverageDir(t), "exitCode")
|
||||||
|
_, err := os.Stat(f)
|
||||||
|
if err != nil {
|
||||||
|
t.Log(err)
|
||||||
|
return orig
|
||||||
|
}
|
||||||
|
// Remove the file, ready for the next test
|
||||||
|
//defer os.Remove(f)
|
||||||
|
buf, err := ioutil.ReadFile(f)
|
||||||
|
if err != nil {
|
||||||
|
t.Log(err)
|
||||||
|
return orig
|
||||||
|
}
|
||||||
|
rc, err := strconv.Atoi(string(buf))
|
||||||
|
if err != nil {
|
||||||
|
t.Log(err)
|
||||||
|
return orig
|
||||||
|
}
|
||||||
|
t.Logf("Retrieved exit code %v from file", rc)
|
||||||
|
return int64(rc)
|
||||||
|
}
|
||||||
|
|
||||||
// waitForContainer waits until a container has exited
|
// waitForContainer waits until a container has exited
|
||||||
func waitForContainer(t *testing.T, cli *client.Client, ID string, timeout int64) int64 {
|
func waitForContainer(t *testing.T, cli *client.Client, ID string, timeout int64) int64 {
|
||||||
//ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
|
//ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
|
||||||
//defer cancel()
|
//defer cancel()
|
||||||
rc, err := cli.ContainerWait(context.Background(), ID)
|
rc, err := cli.ContainerWait(context.Background(), ID)
|
||||||
|
|
||||||
|
// COVERAGE: When running coverage, the exit code is written to a file,
|
||||||
|
// to allow the coverage to be generated (which doesn't happen for non-zero
|
||||||
|
// exit codes)
|
||||||
|
rc = getCoverageExitCode(t, rc)
|
||||||
|
|
||||||
// err := <-errC
|
// err := <-errC
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|||||||
Reference in New Issue
Block a user