Compare commits

...

60 Commits

Author SHA1 Message Date
Rob Parker
a9718af565 Merge pull request #301 from ibm-messaging/master
Backport fixes to 9.1.2
2019-04-10 14:25:20 +01:00
Rob Parker
f05a7d3eaf Merge pull request #300 from sdmarshall79/redact-mqsc-more
Handle multiple-spaces when redacting MQSC output
2019-04-09 15:43:49 +01:00
Stephen Marshall
723fe2b998 Handle multiple-spaces when redacting MQSC output 2019-04-09 15:06:25 +01:00
Stephen Marshall
44d0e0a432 Redact sensitive MQSC logs 2019-04-08 08:49:06 +01:00
LPowlett
33defc0fc9 Handle large mqsc files (#295)
* buffer mqsc input to runmqsc

* imports

* error handling on runmqsc
2019-04-04 16:46:07 +01:00
LPowlett
d69befed71 Version 9.1.2.0 (#294)
* Version 9.1.2.0
2019-03-21 18:25:08 +00:00
Rob Parker
0934289b61 Merge pull request #292 from LPowlett/912-release
912 release manifest update
2019-03-21 16:41:11 +00:00
Luke Powlett
f6231cd51c cert expiry 2019-03-21 16:15:31 +00:00
Luke Powlett
d5f04bc470 copyright notice update 2019-03-21 09:57:59 +00:00
Luke Powlett
d8cbf4566e 9.1.2 manifest update 2019-03-21 09:56:14 +00:00
Rob Parker
493bc7bfd4 Merge pull request #286 from parrobe/gosec
fix gosec failures
2019-03-08 13:36:54 +00:00
Robert Parker
1fa4f6f148 fix gosec failures 2019-03-08 12:59:54 +00:00
Rob Parker
ce664dd654 Merge pull request #285 from parrobe/redistdownload
Fixes for builds
2019-03-08 12:47:11 +00:00
Robert Parker
f8e057a1d6 Fix test breaks from unused code 2019-03-08 11:37:45 +00:00
Robert Parker
efd550822d merge latest from master 2019-03-08 11:13:14 +00:00
Robert Parker
d4df05fd2a Add test fixes and fix mq-explorer build 2019-03-08 11:07:44 +00:00
LPowlett
194b04ac13 Revert MQSC error check (#283)
* Revert MQSC error check
2019-03-08 09:13:26 +00:00
Robert Parker
6848038165 hard code the redist client version 2019-03-06 10:03:38 +00:00
Rob Parker
0d3e177147 Merge pull request #282 from arthurbarr/master
Fix POWER and z/Linux builds
2019-03-06 09:58:15 +00:00
Arthur Barr
13f620f21a Increase test timeouts 2019-03-05 15:08:07 +00:00
Arthur Barr
d4a81741cc Prevent re-download of image during build 2019-03-05 15:07:51 +00:00
Arthur Barr
0047301335 Use Makefile target build-sdk-ex 2019-03-05 11:31:27 +00:00
Arthur Barr
5ef532d2c1 Don't use redist client for Go SDK because of P+Z support 2019-03-05 10:19:21 +00:00
Arthur Barr
281cdc4578 Update RHEL build README 2019-03-05 09:47:58 +00:00
Rob Parker
d68c051104 Merge pull request #280 from parrobe/rhelpackages
Add in system to generate licenses on RHEL
2019-03-04 16:00:49 +00:00
Rob Parker
c5a52e616c Merge branch 'master' into rhelpackages 2019-02-27 15:35:11 +00:00
Arthur Barr
c441de7d26 Fixes for tests on RHEL and test log output 2019-02-27 15:32:34 +00:00
Arthur Barr
a194545f08 Don't add root user into mqm group 2019-02-27 15:32:34 +00:00
Arthur Barr
4f57d1bae2 Tidy up commented code 2019-02-27 15:32:34 +00:00
Arthur Barr
cc0f072908 Change for running as a non-root user (#276)
* Enable running container as mqm

* Fix merge problem

* Don't force root usage

* RHEL image runs as mqm instead of root

* Build on host with SELinux enabled

* Enable building on node in an OpenShift cluster

* Enable running container as mqm

* Fix merge problem

* Don't force root usage

* Merge lastest changes from master

* RHEL image runs as mqm instead of root

* Fix merge issues

* Test changes for non-root

* Make timeout properly, and more non-root test fixes

* Run tests with fewer/no capabilities

* Correct usage docs for non-root

* Add security docs

* Add temporary debug output

* Remove debug code

* Fixes for termination-log

* Allow init container to run as root

* Fixes for CentOS build

* Fixes for RHEL build

* Logging improvements

* Fix Dockerfile RHEL/CentOS build

* Fix bash error

* Make all builds specify UID

* Use redist client for Go SDK

* Inspect image before running tests

* New test for init container

* Log container runtime in runmqdevserver

* Add extra capabilities if using a RHEL image
2019-02-27 15:32:34 +00:00
Robert Parker
d834ac7c9c Add in system to generate licenses on RHEL 2019-02-27 15:06:09 +00:00
Rob Parker
2dbee560fe Merge pull request #272 from parrobe/master
fix dockerfile reference to build explorer
2019-02-12 11:33:20 +00:00
Arthur Barr
17d3238161 Merge branch 'master' into master 2019-02-07 17:07:45 +00:00
Luke Powlett
c08ca2e79f update travis to use Ubuntu 16.04 2019-02-07 16:47:32 +00:00
Rob Parker
84df0e8362 Merge branch 'master' into master 2019-02-07 11:41:51 +00:00
Rob Parker
cc213f429f Merge pull request #273 from arthurbarr/make
Fixes for RHEL build
2019-02-07 11:41:34 +00:00
Rob Parker
c29159dd38 Merge branch 'master' into make 2019-02-07 10:49:02 +00:00
Rob Parker
f345ccf920 Merge branch 'master' into master 2019-02-07 10:48:51 +00:00
Rob Parker
d1b1cfc5d8 Merge pull request #274 from parrobe/golint
Use documented way to install golint
2019-02-07 10:48:39 +00:00
Robert Parker
a19c455ea4 up the copyright date 2019-02-07 10:29:41 +00:00
Robert Parker
694b31d6e8 Use documented way to install golint 2019-02-07 10:09:53 +00:00
Arthur Barr
33f82d76ff Use variable for uid and gid 2019-02-06 16:12:18 +00:00
Arthur Barr
77319629fe Update copyright dates 2019-02-06 16:11:52 +00:00
Arthur Barr
d9c70c48c5 Fix subscription checking logic 2019-02-06 14:23:25 +00:00
Arthur Barr
599f5f4b53 Build on host with SELinux enabled 2019-02-06 13:39:42 +00:00
Arthur Barr
6840a575f9 Fix for building on subscribed and unsubscribed hosts 2019-02-06 13:29:44 +00:00
Robert Parker
1b8c816f57 fix dockerfile reference to build explorer 2019-02-05 15:54:31 +00:00
Arthur Barr
9a8ff9b524 copy sub-directories under /etc/yum.repos.d 2019-02-05 13:53:59 +00:00
Arthur Barr
f3c858184f use correct download files in RHEL build process 2019-02-05 13:51:15 +00:00
LPowlett
43676049b7 RHEL Security Vulnerability Test (#270)
* check for security vulnerabilities on rhel

* import

* check host is red hat

* filepath join

* imports
2019-02-04 10:25:55 +00:00
LPowlett
df6ce917c2 endmqm -r try to reconnect (#268) 2019-01-28 11:03:54 +00:00
Rob Parker
d3eb6e0d3d Merge pull request #267 from arthurbarr/master
RHEL build uses redist client and RHEL minimal
2019-01-25 13:49:46 +00:00
Arthur Barr
2bfdd51a01 RHEL build uses redist client and RHEL minimal 2019-01-24 13:51:10 +00:00
LPowlett
be11b3cda1 Dependancy upgrades (#264)
* dependancy upgrades

* revert dep to 0.4.1

* revert file changes
2019-01-10 14:55:22 +00:00
LPowlett
525ff82fe7 Error checking invalid mqsc commands (#261)
* testRepeatingMqsc

* testInvalidMqsc

* 9.1.1 Makefile

* testRepeatingMqsc

* testInvalidMqsc

* update copyright 2019

* update invalid mqsc error message

* update changelog to reflect MQSC changes
2019-01-10 13:41:43 +00:00
Rob Parker
3e07814bf6 update travis build stages to remove duplicate build and label (#263)
* update travis build stages to remove duplicate build and label

* update copyright to 2019
2019-01-10 10:21:20 +00:00
Rob Parker
b1daacf377 Merge pull request #256 from parrobe/releasetag
Add image tag into info output, add ability to print info output on d…
2018-12-12 13:51:26 +00:00
Robert Parker
9c8b3825be Add image tag into info output, add ability to print info output on demand and prevent multiple instances of runmqserver being run 2018-12-12 13:11:47 +00:00
Rob Parker
4145f077b6 update perl-base to fix security vulnerability (#253) 2018-12-05 13:01:31 +00:00
Robert Parker
c063ddd67d remove deprecated tag manifest 2018-12-03 11:31:24 +00:00
59 changed files with 1570 additions and 712 deletions

View File

@@ -1,4 +1,4 @@
# © Copyright IBM Corporation 2018
# © Copyright IBM Corporation 2018, 2019
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -12,6 +12,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
dist: xenial
sudo: required
language: go
@@ -27,14 +29,19 @@ cache:
directories:
- downloads
env:
- BASE_IMAGE=ubuntu:16.04
- BASE_IMAGE=centos:latest
jobs:
include:
- if: type IN (pull_request)
env: DOCKER_DOWNGRADE="docker save -o images.tar mqadvanced-server-dev mq-dev-jms-test &&
- stage: build and test
env:
- BASE_IMAGE=ubuntu:16.04
- DOCKER_DOWNGRADE="echo nothing to be done"
- env:
- BASE_IMAGE=centos:7
- DOCKER_DOWNGRADE="echo nothing to be done"
- if: type IN (pull_request) OR tag IS present
env:
- BASE_IMAGE=ubuntu:16.04
- DOCKER_DOWNGRADE="docker save -o images.tar mqadvanced-server-dev mq-dev-jms-test &&
sudo apt-get autoremove -y docker-ce &&
curl -fsSL \"https://apt.dockerproject.org/gpg\" | sudo apt-key add - &&
sudo apt-add-repository \"deb https://apt.dockerproject.org/repo ubuntu-$(lsb_release -cs) main\" &&
@@ -42,7 +49,6 @@ jobs:
sudo apt-get install docker-engine=1.12.6-0~ubuntu-$(lsb_release -cs) &&
docker load -q -i images.tar &&
export DOCKER_API_VERSION=\"1.24\""
- env: DOCKER_DOWNGRADE="echo nothing to be done"
before_install:
- ./install-build-deps-ubuntu.sh

View File

@@ -1,11 +1,23 @@
# Change log
## 9.1.2.0 (2019-03-21)
* Now runs using the "mqm" user instead of root. See new [security doc](https://github.com/ibm-messaging/mq-container/blob/master/docs/security.md)
* New [IGNSTATE](https://www.ibm.com/support/knowledgecenter/en/SSFKSJ_9.1.0/com.ibm.mq.pro.doc/q132310_.htm#q132310___ignstateparm) parameter used in default developer config
* Termination log moved from `/dev/termination-log` to `/run/termination-log`, to make permissions easier to handle
* Fixes for the following issues:
* Brackets no longer appear in termination log
* Test timeouts weren't being used correctly
* Building on subscribed and unsubscribed hosts ([#273](https://github.com/ibm-messaging/mq-container/pull/273))
* Gosec failures ([#286](https://github.com/ibm-messaging/mq-container/pull/286))
* Security fix for perl-base ([#253](https://github.com/ibm-messaging/mq-container/pull/253))
## 9.1.1.0 (2018-11-30)
* Updated to MQ version 9.1.1.0
* Created seperate RedHat Makefile for building images on RedHat machines with buildah
* Enabled REST messaging capability for app user.
* Added support for container suplimentary groups
* Added support for container supplementary groups
* Removed IBM MQ version 9.0.5 details.
* Added additional Diagnostics ([#203](https://github.com/ibm-messaging/mq-container/pull/203))
* Implementted GOSec to perform code scans for security vulnerabilities. (([#227](https://github.com/ibm-messaging/mq-container/pull/227)))

View File

@@ -1,4 +1,4 @@
# © Copyright IBM Corporation 2015, 2018
# © Copyright IBM Corporation 2015, 2019
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -13,7 +13,7 @@
# limitations under the License.
ARG BASE_IMAGE=ubuntu:16.04
ARG BUILDER_IMAGE=mq-golang-sdk:9.1.1.0-x86_64-ubuntu-16.04
ARG BUILDER_IMAGE=mq-golang-sdk:9.1.2.0-x86_64-ubuntu-16.04
###############################################################################
# Build stage to build Go code
@@ -22,10 +22,11 @@ FROM $BUILDER_IMAGE as builder
WORKDIR /go/src/github.com/ibm-messaging/mq-container/
ARG IMAGE_REVISION="Not specified"
ARG IMAGE_SOURCE="Not specified"
ARG IMAGE_TAG="Not specified"
COPY cmd/ ./cmd
COPY internal/ ./internal
COPY vendor/ ./vendor
RUN go build -ldflags "-X \"main.ImageCreated=$(date --iso-8601=seconds)\" -X \"main.ImageRevision=$IMAGE_REVISION\" -X \"main.ImageSource=$IMAGE_SOURCE\"" ./cmd/runmqserver/
RUN go build -ldflags "-X \"main.ImageCreated=$(date --iso-8601=seconds)\" -X \"main.ImageRevision=$IMAGE_REVISION\" -X \"main.ImageSource=$IMAGE_SOURCE\" -X \"main.ImageTag=$IMAGE_TAG\"" ./cmd/runmqserver/
RUN go build ./cmd/chkmqready/
RUN go build ./cmd/chkmqhealthy/
# Run all unit tests
@@ -47,12 +48,15 @@ ARG MQ_URL
# The MQ packages to install - see install-mq.sh for default value
ARG MQ_PACKAGES
# The UID to use for the "mqm" user
ARG MQM_UID=999
COPY install-mq.sh /usr/local/bin/
# Install MQ. To avoid a "text file busy" error here, we sleep before installing.
RUN chmod u+x /usr/local/bin/install-mq.sh \
&& sleep 1 \
&& install-mq.sh
&& install-mq.sh $MQM_UID
# Create a directory for runtime data from runmqserver
RUN mkdir -p /run/runmqserver \
@@ -64,11 +68,17 @@ COPY NOTICES.txt /opt/mqm/licenses/notices-container.txt
RUN chmod ug+x /usr/local/bin/runmqserver \
&& chown mqm:mqm /usr/local/bin/*mq* \
&& chmod ug+xs /usr/local/bin/chkmq*
&& chmod ug+xs /usr/local/bin/chkmq* \
&& install --directory --mode 0775 --owner mqm --group root /run/runmqserver \
&& touch /run/termination-log \
&& chown mqm:root /run/termination-log \
&& chmod 0660 /run/termination-log
# Always use port 1414 for MQ & 9157 for the metrics
EXPOSE 1414 9157
ENV LANG=en_US.UTF-8 AMQ_DIAGNOSTIC_MSG_SEVERITY=1 AMQ_ADDITIONAL_JSON_LOG=1 LOG_FORMAT=basic
USER $MQM_UID
ENTRYPOINT ["runmqserver"]

View File

@@ -176,7 +176,7 @@
END OF TERMS AND CONDITIONS
© Copyright IBM Corporation. 2015, 2018
© Copyright IBM Corporation. 2015, 2019
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@@ -1,4 +1,4 @@
# © Copyright IBM Corporation 2018
# © Copyright IBM Corporation 2018, 2019
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -19,16 +19,16 @@
# BASE_IMAGE is the base image to use for MQ, for example "ubuntu" or "rhel"
BASE_IMAGE ?= rhel
# MQ_VERSION is the fully qualified MQ version number to build
MQ_VERSION ?= 9.1.1.0
MQ_VERSION ?= 9.1.2.0
# MQ_ARCHIVE is the name of the file, under the downloads directory, from which MQ Advanced can
# be installed. The default value is derived from MQ_VERSION, BASE_IMAGE and architecture
# Does not apply to MQ Advanced for Developers.
MQ_ARCHIVE ?= IBM_MQ_$(MQ_VERSION)_LINUX_$(MQ_ARCHIVE_ARCH).tar.gz
MQ_ARCHIVE ?= IBM_MQ_$(MQ_VERSION_VRM)_LINUX_$(MQ_ARCHIVE_ARCH).tar.gz
# MQ_ARCHIVE_DEV is the name of the file, under the downloads directory, from which MQ Advanced
# for Developers can be installed
MQ_ARCHIVE_DEV ?= $(MQ_ARCHIVE_DEV_$(MQ_VERSION))
# MQ_SDK_ARCHIVE specifies the archive to use for building the golang programs. Defaults vary on developer or advanced.
MQ_SDK_ARCHIVE ?= $(MQ_ARCHIVE_DEV_$(MQ_VERSION))
# MQ_SDK_ARCHIVE specifies the archive to use for the MQ redistributable client, which is used for building the golang programs.
MQ_SDK_ARCHIVE ?= 9.1.2.0-IBM-MQC-Redist-LinuxX64.tar.gz
# Options to `go test` for the Docker tests
TEST_OPTS_DOCKER ?=
# MQ_IMAGE_ADVANCEDSERVER is the name and tag of the built MQ Advanced image
@@ -56,6 +56,10 @@ DEV_JMS_IMAGE=mq-dev-jms-test:latest
IMAGE_REVISION=$(shell git rev-parse HEAD)
IMAGE_SOURCE=$(shell git config --get remote.origin.url)
MQDEV=
EMPTY:=
SPACE:= $(EMPTY) $(EMPTY)
# MQ_VERSION_VRM is MQ_VERSION with only the Version, Release and Modifier fields (no Fix field). e.g. 9.1.2 instead of 9.1.2.0
MQ_VERSION_VRM=$(subst $(SPACE),.,$(wordlist 1,3,$(subst .,$(SPACE),$(MQ_VERSION))))
ifneq (,$(findstring Microsoft,$(shell uname -r)))
@@ -76,9 +80,9 @@ else ifeq "$(ARCH)" "s390x"
MQ_DEV_ARCH=s390x
endif
# Archive names for IBM MQ Advanced for Developers
MQ_ARCHIVE_DEV_9.0.5.0=mqadv_dev905_linux_x86-64.tar.gz
MQ_ARCHIVE_DEV_9.1.0.0=mqadv_dev910_linux_$(MQ_DEV_ARCH).tar.gz
MQ_ARCHIVE_DEV_9.1.1.0=mqadv_dev910_linux_$(MQ_DEV_ARCH).tar.gz
MQ_ARCHIVE_DEV_9.1.1.0=mqadv_dev911_linux_$(MQ_DEV_ARCH).tar.gz
MQ_ARCHIVE_DEV_9.1.2.0=mqadv_dev912_linux_$(MQ_DEV_ARCH).tar.gz
###############################################################################
# Build targets
@@ -113,9 +117,9 @@ downloads/$(MQ_ARCHIVE_DEV):
cd downloads; curl -LO https://public.dhe.ibm.com/ibmdl/export/pub/software/websphere/messaging/mqadv/$(MQ_ARCHIVE_DEV)
downloads/$(MQ_SDK_ARCHIVE):
$(info $(SPACER)$(shell printf $(TITLE)"Downloading IBM MQ Advanced for Developers "$(MQ_VERSION)$(END)))
$(info $(SPACER)$(shell printf $(TITLE)"Downloading IBM MQ Advanced redistributable client "$(MQ_VERSION)$(END)))
mkdir -p downloads
cd downloads; curl -LO https://public.dhe.ibm.com/ibmdl/export/pub/software/websphere/messaging/mqadv/$(MQ_SDK_ARCHIVE)
cd downloads; curl -LO https://public.dhe.ibm.com/ibmdl/export/pub/software/websphere/messaging/mqdev/redist/$(MQ_SDK_ARCHIVE)
.PHONY: downloads
downloads: downloads/$(MQ_ARCHIVE_DEV) downloads/$(MQ_SDK_ARCHIVE)
@@ -133,7 +137,7 @@ check-prereqs:
yum list | grep yum-utils || (echo "Missing required package yum-utils" && exit 1)
.PHONY: check-test-prereqs
check-prereqs:
check-test-prereqs:
$(info $(SPACER)$(shell printf $(TITLE)"Checking for prereqs"$(END)))
which buildah || (echo "Missing required program buildah" && exit 1)
which docker || (echo "Missing required program docker" && exit 1)
@@ -155,37 +159,29 @@ test-devserver: check-test-prereqs test/docker/vendor
.PHONY: build-advancedserver
build-advancedserver: MQ_SDK_ARCHIVE=$(MQ_ARCHIVE)
build-advancedserver: check-prereqs downloads/$(MQ_ARCHIVE) build-go-programs-ex
build-advancedserver: check-prereqs downloads/$(MQ_ARCHIVE) build-go-programs
$(info $(SPACER)$(shell printf $(TITLE)"Build $(MQ_IMAGE_ADVANCEDSERVER)"$(END)))
sudo mq-advanced-server-rhel/mq-buildah.sh "$(MQ_ARCHIVE)" "$(MQ_PACKAGES)" "$(MQ_IMAGE_ADVANCEDSERVER)" "$(MQ_VERSION)" "$(MQDEV)"
.PHONY: build-devserver
build-devserver: MQ_SDK_ARCHIVE=$(MQ_ARCHIVE_DEV)
build-devserver: MQDEV=TRUE
build-devserver: MQ_PACKAGES=MQSeriesRuntime-*.rpm MQSeriesServer-*.rpm MQSeriesJava*.rpm MQSeriesJRE*.rpm MQSeriesGSKit*.rpm MQSeriesMsg*.rpm MQSeriesSamples*.rpm MQSeriesAMS-*.rpm MQSeriesWeb-*.rpm
build-devserver: check-prereqs downloads/$(MQ_ARCHIVE_DEV) build-go-programs-ex
build-devserver: check-prereqs downloads/$(MQ_ARCHIVE_DEV) build-go-programs
$(info $(SPACER)$(shell printf $(TITLE)"Build $(MQ_IMAGE_DEVSERVER)"$(END)))
sudo mq-advanced-server-rhel/mq-buildah.sh "$(MQ_ARCHIVE_DEV)" "$(MQ_PACKAGES)" "$(MQ_IMAGE_DEVSERVER_BASE)" "$(MQ_VERSION)" "$(MQDEV)"
sudo mq-advanced-server-rhel/mqdev-buildah.sh "$(MQ_IMAGE_DEVSERVER_BASE)" "$(MQ_IMAGE_DEVSERVER)" "$(MQ_VERSION)"
.PHONY: build-mqgolang-sdk
build-mqgolang-sdk: check-prereqs downloads/$(MQ_SDK_ARCHIVE) build-mqgolang-sdk-ex
.PHONY: build-mqgolang-sdk-ex
build-mqgolang-sdk-ex:
build-mqgolang-sdk: check-prereqs downloads/$(MQ_SDK_ARCHIVE)
$(info $(SPACER)$(shell printf $(TITLE)"Build mq-golang SDK"$(END)))
sudo mq-advanced-server-rhel/mq-golang-sdk-buildah.sh "$(MQ_SDK_ARCHIVE)" "$(MQ_IMAGE_GOLANG_SDK)"
.PHONY: build-go-programs
build-go-programs: check-prereqs downloads/$(MQ_SDK_ARCHIVE) build-go-programs-ex
.PHONY: build-go-programs-ex
build-go-programs-ex: build-mqgolang-sdk-ex
build-go-programs: check-prereqs downloads/$(MQ_SDK_ARCHIVE) build-mqgolang-sdk
$(info $(SPACER)$(shell printf $(TITLE)"Build go programs"$(END)))
IMAGE_REVISION=$(IMAGE_REVISION) IMAGE_SOURCE=$(IMAGE_SOURCE) sudo mq-advanced-server-rhel/go-buildah.sh "$(MQ_IMAGE_GOLANG_SDK)" "$(MQDEV)"
IMAGE_REVISION=$(IMAGE_REVISION) IMAGE_SOURCE=$(IMAGE_SOURCE) sudo --preserve-env mq-advanced-server-rhel/go-buildah.sh "$(MQ_IMAGE_GOLANG_SDK)" "$(MQDEV)"
.PHONY: build-devjmstest
build-devjmstest: check-test-prereqs
@@ -194,4 +190,15 @@ build-devjmstest: check-test-prereqs
sudo buildah push $(DEV_JMS_IMAGE) docker-daemon:$(DEV_JMS_IMAGE)
docker tag docker.io/$(DEV_JMS_IMAGE) $(DEV_JMS_IMAGE)
.PHONY: debug-vars
debug-vars:
@echo MQ_VERSION=$(MQ_VERSION)
@echo MQ_VERSION_VRM=$(MQ_VERSION_VRM)
@echo MQ_ARCHIVE=$(MQ_ARCHIVE)
@echo MQ_SDK_ARCHIVE=$(MQ_SDK_ARCHIVE)
@echo MQ_IMAGE_GOLANG_SDK=$(MQ_IMAGE_GOLANG_SDK)
@echo MQ_IMAGE_DEVSERVER_BASE=$(MQ_IMAGE_DEVSERVER_BASE)
@echo MQ_IMAGE_DEVSERVER=$(MQ_IMAGE_DEVSERVER)
@echo MQ_IMAGE_ADVANCEDSERVER=$(MQ_IMAGE_ADVANCEDSERVER)
include formatting.mk

View File

@@ -1,4 +1,4 @@
# © Copyright IBM Corporation 2017, 2018
# © Copyright IBM Corporation 2017, 2019
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -19,11 +19,11 @@
# BASE_IMAGE is the base image to use for MQ, for example "ubuntu" or "rhel"
BASE_IMAGE ?= ubuntu:16.04
# MQ_VERSION is the fully qualified MQ version number to build
MQ_VERSION ?= 9.1.1.0
MQ_VERSION ?= 9.1.2.0
# MQ_ARCHIVE is the name of the file, under the downloads directory, from which MQ Advanced can
# be installed. The default value is derived from MQ_VERSION, BASE_IMAGE and architecture
# Does not apply to MQ Advanced for Developers.
MQ_ARCHIVE ?= IBM_MQ_$(MQ_VERSION)_$(MQ_ARCHIVE_TYPE)_$(MQ_ARCHIVE_ARCH).tar.gz
MQ_ARCHIVE ?= IBM_MQ_$(MQ_VERSION_VRM)_$(MQ_ARCHIVE_TYPE)_$(MQ_ARCHIVE_ARCH).tar.gz
# MQ_ARCHIVE_DEV is the name of the file, under the downloads directory, from which MQ Advanced
# for Developers can be installed
MQ_ARCHIVE_DEV ?= $(MQ_ARCHIVE_DEV_$(MQ_VERSION))
@@ -62,6 +62,10 @@ DEV_JMS_IMAGE=mq-dev-jms-test
# Variables for versioning
IMAGE_REVISION=$(shell git rev-parse HEAD)
IMAGE_SOURCE=$(shell git config --get remote.origin.url)
EMPTY:=
SPACE:= $(EMPTY) $(EMPTY)
# MQ_VERSION_VRM is MQ_VERSION with only the Version, Release and Modifier fields (no Fix field). e.g. 9.1.2 instead of 9.1.2.0
MQ_VERSION_VRM=$(subst $(SPACE),.,$(wordlist 1,3,$(subst .,$(SPACE),$(MQ_VERSION))))
ifneq (,$(findstring Microsoft,$(shell uname -r)))
DOWNLOADS_DIR=$(patsubst /mnt/c%,C:%,$(realpath ./downloads/))
@@ -73,9 +77,11 @@ endif
ifeq "$(findstring ubuntu,$(BASE_IMAGE))" "ubuntu"
MQ_ARCHIVE_TYPE=UBUNTU
MQ_ARCHIVE_DEV_PLATFORM=ubuntu
MQM_UID=999
else
MQ_ARCHIVE_TYPE=LINUX
MQ_ARCHIVE_DEV_PLATFORM=linux
MQM_UID=888
endif
# Try to figure out which archive to use from the architecture
ifeq "$(ARCH)" "x86_64"
@@ -89,9 +95,9 @@ else ifeq "$(ARCH)" "s390x"
MQ_DEV_ARCH=s390x
endif
# Archive names for IBM MQ Advanced for Developers
MQ_ARCHIVE_DEV_9.0.5.0=mqadv_dev905_$(MQ_ARCHIVE_DEV_PLATFORM)_x86-64.tar.gz
MQ_ARCHIVE_DEV_9.1.0.0=mqadv_dev910_$(MQ_ARCHIVE_DEV_PLATFORM)_$(MQ_DEV_ARCH).tar.gz
MQ_ARCHIVE_DEV_9.1.1.0=mqadv_dev911_$(MQ_ARCHIVE_DEV_PLATFORM)_$(MQ_DEV_ARCH).tar.gz
MQ_ARCHIVE_DEV_9.1.2.0=mqadv_dev912_$(MQ_ARCHIVE_DEV_PLATFORM)_$(MQ_DEV_ARCH).tar.gz
###############################################################################
# Build targets
@@ -145,6 +151,7 @@ test-unit:
.PHONY: test-advancedserver
test-advancedserver: test/docker/vendor
$(info $(SPACER)$(shell printf $(TITLE)"Test $(MQ_IMAGE_ADVANCEDSERVER) on $(shell docker --version)"$(END)))
docker inspect $(MQ_IMAGE_ADVANCEDSERVER)
cd test/docker && TEST_IMAGE=$(MQ_IMAGE_ADVANCEDSERVER) EXPECTED_LICENSE=Production go test -parallel $(NUM_CPU) $(TEST_OPTS_DOCKER)
.PHONY: build-devjmstest
@@ -155,6 +162,7 @@ build-devjmstest:
.PHONY: test-devserver
test-devserver: test/docker/vendor
$(info $(SPACER)$(shell printf $(TITLE)"Test $(MQ_IMAGE_DEVSERVER) on $(shell docker --version)"$(END)))
docker inspect $(MQ_IMAGE_DEVSERVER)
cd test/docker && TEST_IMAGE=$(MQ_IMAGE_DEVSERVER) EXPECTED_LICENSE=Developer DEV_JMS_IMAGE=$(DEV_JMS_IMAGE) IBMJRE=true go test -parallel $(NUM_CPU) -tags mqdev $(TEST_OPTS_DOCKER)
coverage:
@@ -205,6 +213,8 @@ define docker-build-mq
--build-arg BUILDER_IMAGE=$(MQ_IMAGE_GOLANG_SDK) \
--build-arg IMAGE_REVISION="$(IMAGE_REVISION)" \
--build-arg IMAGE_SOURCE="$(IMAGE_SOURCE)" \
--build-arg IMAGE_TAG="$1" \
--build-arg MQM_UID=$(MQM_UID) \
--label IBM_PRODUCT_ID=$4 \
--label IBM_PRODUCT_NAME=$5 \
--label IBM_PRODUCT_VERSION=$6 \
@@ -236,15 +246,20 @@ build-devserver: MQ_SDK_ARCHIVE=$(MQ_ARCHIVE_DEV)
build-devserver: downloads/$(MQ_ARCHIVE_DEV) docker-version build-golang-sdk-ex
$(info $(shell printf $(TITLE)"Build $(MQ_IMAGE_DEVSERVER_BASE)"$(END)))
$(call docker-build-mq,$(MQ_IMAGE_DEVSERVER_BASE),Dockerfile-server,$(MQ_ARCHIVE_DEV),"98102d16795c4263ad9ca075190a2d4d","IBM MQ Advanced for Developers (Non-Warranted)",$(MQ_VERSION))
$(DOCKER) build --tag $(MQ_IMAGE_DEVSERVER) --build-arg IMAGE_SOURCE="$(IMAGE_SOURCE)" --build-arg IMAGE_REVISION="$(IMAGE_REVISION)" --build-arg BASE_IMAGE=$(MQ_IMAGE_DEVSERVER_BASE) --build-arg BUILDER_IMAGE=$(MQ_IMAGE_GOLANG_SDK) --file incubating/mqadvanced-server-dev/Dockerfile .
$(DOCKER) build --tag $(MQ_IMAGE_DEVSERVER) --build-arg IMAGE_SOURCE="$(IMAGE_SOURCE)" --build-arg IMAGE_REVISION="$(IMAGE_REVISION)" --build-arg IMAGE_TAG="$(MQ_IMAGE_DEVSERVER)" --build-arg BASE_IMAGE=$(MQ_IMAGE_DEVSERVER_BASE) --build-arg BUILDER_IMAGE=$(MQ_IMAGE_GOLANG_SDK) --build-arg MQM_UID=$(MQM_UID) --file incubating/mqadvanced-server-dev/Dockerfile .
.PHONY: build-advancedserver-cover
build-advancedserver-cover: docker-version
$(DOCKER) build --build-arg BASE_IMAGE=$(MQ_IMAGE_ADVANCEDSERVER) -t $(MQ_IMAGE_ADVANCEDSERVER)-cover -f Dockerfile-server.cover .
.PHONY: build-explorer
ifeq "$(findstring ubuntu,$(BASE_IMAGE))" "ubuntu"
build-explorer: MQ_PACKAGES=ibmmq-explorer
else
build-explorer: MQ_PACKAGES=MQSeriesRuntime*.rpm MQSeriesJRE*.rpm MQSeriesExplorer*.rpm
endif
build-explorer: downloads/$(MQ_ARCHIVE_DEV) docker-pull
$(call docker-build-mq,mq-explorer:latest-$(ARCH),incubating/mq-explorer/Dockerfile-mq-explorer,$(MQ_ARCHIVE_DEV),"98102d16795c4263ad9ca075190a2d4d","IBM MQ Advanced for Developers (Non-Warranted)",$(MQ_VERSION))
$(call docker-build-mq,mq-explorer:latest-$(ARCH),incubating/mq-explorer/Dockerfile,$(MQ_ARCHIVE_DEV),"98102d16795c4263ad9ca075190a2d4d","IBM MQ Advanced for Developers (Non-Warranted)",$(MQ_VERSION))
.PHONY: build-sdk
build-sdk: downloads/$(MQ_SDK_ARCHIVE) build-sdk-ex
@@ -256,6 +271,7 @@ else
build-sdk-ex: MQ_PACKAGES=MQSeriesRuntime-*.rpm MQSeriesSDK-*.rpm MQSeriesSamples*.rpm
endif
build-sdk-ex: docker-version docker-pull
$(info $(shell printf $(TITLE)"Build $(MQ_IMAGE_SDK)"$(END)))
$(call docker-build-mq,$(MQ_IMAGE_SDK),incubating/mq-sdk/Dockerfile,$(MQ_SDK_ARCHIVE),"98102d16795c4263ad9ca075190a2d4d","IBM MQ Advanced for Developers SDK (Non-Warranted)",$(MQ_VERSION))
.PHONY: build-golang-sdk
@@ -263,8 +279,10 @@ build-golang-sdk: downloads/$(MQ_SDK_ARCHIVE) build-golang-sdk-ex
.PHONY: build-golang-sdk-ex
build-golang-sdk-ex: docker-version build-sdk-ex
$(info $(shell printf $(TITLE)"Build $(MQ_IMAGE_GOLANG_SDK)"$(END)))
@echo hello
$(DOCKER) build --build-arg BASE_IMAGE=$(MQ_IMAGE_SDK) -t $(MQ_IMAGE_GOLANG_SDK) -f incubating/mq-golang-sdk/Dockerfile .
# $(call docker-build-mq,$(MQ_IMAGE_GOLANG_SDK),incubating/mq-golang-sdk/Dockerfile,$(MQ_IMAGE_SDK),"98102d16795c4263ad9ca075190a2d4d","IBM MQ Advanced for Developers SDK (Non-Warranted)",$(MQ_VERSION))
@echo goodbye
.PHONY: docker-pull
docker-pull:

View File

@@ -0,0 +1,68 @@
/*
© Copyright IBM Corporation 2017, 2019
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 (
"runtime"
"strings"
containerruntime "github.com/ibm-messaging/mq-container/internal/containerruntime"
"github.com/ibm-messaging/mq-container/internal/user"
)
func logContainerDetails() {
log.Printf("CPU architecture: %v", runtime.GOARCH)
kv, err := containerruntime.GetKernelVersion()
if err == nil {
log.Printf("Linux kernel version: %v", kv)
}
cr, err := containerruntime.GetContainerRuntime()
if err == nil {
log.Printf("Container runtime: %v", cr)
}
bi, err := containerruntime.GetBaseImage()
if err == nil {
log.Printf("Base image: %v", bi)
}
u, err := user.GetUser()
if err == nil {
if len(u.SupplementalGID) == 0 {
log.Printf("Running as user ID %v (%v) with primary group %v", u.UID, u.Name, u.PrimaryGID)
} else {
log.Printf("Running as user ID %v (%v) with primary group %v, and supplementary groups %v", u.UID, u.Name, u.PrimaryGID, strings.Join(u.SupplementalGID, ","))
}
}
caps, err := containerruntime.GetCapabilities()
capLogged := false
if err == nil {
for k, v := range caps {
if len(v) > 0 {
log.Printf("Capabilities (%s set): %v", strings.ToLower(k), strings.Join(v, ","))
capLogged = true
}
}
if !capLogged {
log.Print("Capabilities: none")
}
} else {
log.Errorf("Error getting capabilities: %v", err)
}
sc, err := containerruntime.GetSeccomp()
if err == nil {
log.Printf("seccomp enforcing mode: %v", sc)
}
log.Printf("Process security attributes: %v", containerruntime.GetSecurityAttributes())
}

View File

@@ -1,5 +1,5 @@
/*
© Copyright IBM Corporation 2018
© Copyright IBM Corporation 2018, 2019
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -31,7 +31,7 @@ var log *logger.Logger
func setPassword(user string, password string) error {
// #nosec G204
cmd := exec.Command("chpasswd")
cmd := exec.Command("sudo", "chpasswd")
stdin, err := cmd.StdinPipe()
if err != nil {
return err
@@ -41,9 +41,10 @@ func setPassword(user string, password string) error {
if err != nil {
log.Errorf("Error closing password stdin: %v", err)
}
_, _, err = command.RunCmd(cmd)
out, _, err := command.RunCmd(cmd)
if err != nil {
return err
// Include the command output in the error
return fmt.Errorf("%v: %v", err.Error(), out)
}
log.Printf("Set password for \"%v\" user", user)
return nil
@@ -93,16 +94,16 @@ func configureWeb(qmName string) error {
}
func logTerminationf(format string, args ...interface{}) {
logTermination(fmt.Sprintf(format, args))
logTermination(fmt.Sprintf(format, args...))
}
// TODO: Duplicated code
func logTermination(args ...interface{}) {
msg := fmt.Sprint(args)
// Write the message to the termination log. This is the default place
msg := fmt.Sprint(args...)
// Write the message to the termination log. This is not the default place
// that Kubernetes will look for termination information.
log.Debugf("Writing termination message: %v", msg)
err := ioutil.WriteFile("/dev/termination-log", []byte(msg), 0660)
err := ioutil.WriteFile("/run/termination-log", []byte(msg), 0660)
if err != nil {
log.Debug(err)
}
@@ -115,6 +116,9 @@ func doMain() error {
logTermination(err)
return err
}
logContainerDetails()
adminPassword, set := os.LookupEnv("MQ_ADMIN_PASSWORD")
if set {
err = setPassword("admin", adminPassword)
@@ -170,7 +174,7 @@ func main() {
} else {
// Replace this process with runmqserver
// #nosec G204
err = syscall.Exec("/usr/local/bin/runmqserver", []string{"runmqserver"}, os.Environ())
err = syscall.Exec("/usr/local/bin/runmqserver", []string{"runmqserver", "-dev"}, os.Environ())
if err != nil {
log.Errorf("Error replacing this process with runmqserver: %v", err)
}

View File

@@ -1,5 +1,5 @@
/*
© Copyright IBM Corporation 2018
© Copyright IBM Corporation 2018, 2019
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -36,7 +36,8 @@ func processTemplateFile(templateFile, destFile string, data interface{}) error
_, err = os.Stat(dir)
if err != nil {
if os.IsNotExist(err) {
err = os.MkdirAll(dir, 0660)
// #nosec G301
err = os.MkdirAll(dir, 0770)
if err != nil {
log.Error(err)
return err

View File

@@ -1,5 +1,5 @@
/*
© Copyright IBM Corporation 2017, 2018
© Copyright IBM Corporation 2017, 2019
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -36,15 +36,15 @@ var log *logger.Logger
var collectDiagOnFail = false
func logTerminationf(format string, args ...interface{}) {
logTermination(fmt.Sprintf(format, args))
logTermination(fmt.Sprintf(format, args...))
}
func logTermination(args ...interface{}) {
msg := fmt.Sprint(args)
// Write the message to the termination log. This is the default place
msg := fmt.Sprint(args...)
// Write the message to the termination log. This is not the default place
// that Kubernetes will look for termination information.
log.Debugf("Writing termination message: %v", msg)
err := ioutil.WriteFile("/dev/termination-log", []byte(msg), 0660)
err := ioutil.WriteFile("/run/termination-log", []byte(msg), 0660)
if err != nil {
log.Debug(err)
}

View File

@@ -0,0 +1,86 @@
/*
© Copyright IBM Corporation 2017, 2019
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 (
"fmt"
"runtime"
"strings"
containerruntime "github.com/ibm-messaging/mq-container/internal/containerruntime"
"github.com/ibm-messaging/mq-container/internal/user"
)
func logContainerDetails() error {
if runtime.GOOS != "linux" {
return fmt.Errorf("Unsupported platform: %v", runtime.GOOS)
}
log.Printf("CPU architecture: %v", runtime.GOARCH)
kv, err := containerruntime.GetKernelVersion()
if err == nil {
log.Printf("Linux kernel version: %v", kv)
}
cr, err := containerruntime.GetContainerRuntime()
if err == nil {
log.Printf("Container runtime: %v", cr)
}
bi, err := containerruntime.GetBaseImage()
if err == nil {
log.Printf("Base image: %v", bi)
}
u, err := user.GetUser()
if err == nil {
if len(u.SupplementalGID) == 0 {
log.Printf("Running as user ID %v (%v) with primary group %v", u.UID, u.Name, u.PrimaryGID)
} else {
log.Printf("Running as user ID %v (%v) with primary group %v, and supplementary groups %v", u.UID, u.Name, u.PrimaryGID, strings.Join(u.SupplementalGID, ","))
}
}
caps, err := containerruntime.GetCapabilities()
capLogged := false
if err == nil {
for k, v := range caps {
if len(v) > 0 {
log.Printf("Capabilities (%s set): %v", strings.ToLower(k), strings.Join(v, ","))
capLogged = true
}
}
if !capLogged {
log.Print("Capabilities: none")
}
} else {
log.Errorf("Error getting capabilities: %v", err)
}
sc, err := containerruntime.GetSeccomp()
if err == nil {
log.Printf("seccomp enforcing mode: %v", sc)
}
log.Printf("Process security attributes: %v", containerruntime.GetSecurityAttributes())
m, err := containerruntime.GetMounts()
if err == nil {
if len(m) == 0 {
log.Print("No volume detected. Persistent messages may be lost")
} else {
for mountPoint, fsType := range m {
log.Printf("Detected '%v' volume mounted to %v", fsType, mountPoint)
if !containerruntime.SupportedFilesystem(fsType) {
return fmt.Errorf("%v uses unsupported filesystem type: %v", mountPoint, fsType)
}
}
}
}
return nil
}

View File

@@ -1,5 +1,5 @@
/*
© Copyright IBM Corporation 2017, 2018
© Copyright IBM Corporation 2017, 2019
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -20,6 +20,7 @@ package main
import (
"context"
"errors"
"flag"
"os"
"sync"
@@ -29,12 +30,35 @@ import (
)
func doMain() error {
var initFlag = flag.Bool("i", false, "initialize volume only, then exit")
var infoFlag = flag.Bool("info", false, "Display debug info, then exit")
var devFlag = flag.Bool("dev", false, "used when running this program from runmqdevserver to control log output")
flag.Parse()
name, nameErr := name.GetQueueManagerName()
mf, err := configureLogger(name)
if err != nil {
logTermination(err)
return err
}
// Check whether they only want debug info
if *infoFlag {
logVersionInfo()
err = logContainerDetails()
if err != nil {
log.Printf("Error displaying container details: %v", err)
}
return nil
}
err = verifySingleProcess()
if err != nil {
// We don't do the normal termination here as it would create a termination file.
log.Error(err)
return err
}
if nameErr != nil {
logTermination(err)
return err
@@ -61,16 +85,12 @@ func doMain() error {
// Enable diagnostic collecting on failure
collectDiagOnFail = true
err = verifyCurrentUser()
if *devFlag == false {
err = logContainerDetails()
if err != nil {
logTermination(err)
return err
}
err = logConfig()
if err != nil {
logTermination(err)
return err
}
err = createVolume("/mnt/mqm")
@@ -84,6 +104,11 @@ func doMain() error {
return err
}
// If init flag is set, exit now
if *initFlag {
return nil
}
// Print out versioning information
logVersionInfo()

View File

@@ -1,157 +0,0 @@
/*
© Copyright IBM Corporation 2017, 2018
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 (
"fmt"
"io/ioutil"
"runtime"
"strings"
"github.com/genuinetools/amicontained/container"
)
func logContainerRuntime() {
r, err := container.DetectRuntime()
if err != nil {
log.Printf("Failed to get container runtime: %v", err)
return
}
log.Printf("Container runtime: %v", r)
}
func logBaseImage() {
buf, err := ioutil.ReadFile("/etc/os-release")
if err != nil {
log.Printf("Failed to read /etc/os-release: %v", err)
return
}
lines := strings.Split(string(buf), "\n")
for _, l := range lines {
if strings.HasPrefix(l, "PRETTY_NAME=") {
words := strings.Split(l, "\"")
if len(words) >= 2 {
log.Printf("Base image: %v", words[1])
return
}
}
}
}
// logCapabilities logs the Linux capabilities (e.g. setuid, setgid). See https://docs.docker.com/engine/reference/run/#runtime-privilege-and-linux-capabilities
func logCapabilities() {
caps, err := container.Capabilities()
if err != nil {
log.Printf("Failed to get container capabilities: %v", err)
return
}
for k, v := range caps {
if len(v) > 0 {
log.Printf("Capabilities (%s set): %v", strings.ToLower(k), strings.Join(v, ","))
}
}
}
// logSeccomp logs the seccomp enforcing mode, which affects which kernel calls can be made
func logSeccomp() {
s, err := container.SeccompEnforcingMode()
if err != nil {
log.Printf("Failed to get container SeccompEnforcingMode: %v", err)
return
}
log.Printf("seccomp enforcing mode: %v", s)
}
// logSecurityAttributes logs the security attributes of the current process.
// The security attributes indicate whether AppArmor or SELinux are being used,
// and what the level of confinement is.
func logSecurityAttributes() {
a, err := readProc("/proc/self/attr/current")
// On some systems, if AppArmor or SELinux are not installed, you get an
// error when you try and read `/proc/self/attr/current`, even though the
// file exists.
if err != nil || a == "" {
a = "none"
}
log.Printf("Process security attributes: %v", a)
}
func readProc(filename string) (value string, err error) {
// #nosec G304
buf, err := ioutil.ReadFile(filename)
if err != nil {
return "", err
}
return strings.TrimSpace(string(buf)), nil
}
func readMounts() error {
all, err := readProc("/proc/mounts")
if err != nil {
log.Print("Error: Couldn't read /proc/mounts")
return err
}
lines := strings.Split(all, "\n")
detected := false
for i := range lines {
parts := strings.Split(lines[i], " ")
//dev := parts[0]
mountPoint := parts[1]
fsType := parts[2]
if strings.Contains(mountPoint, "/mnt/mqm") {
log.Printf("Detected '%v' volume mounted to %v", fsType, mountPoint)
detected = true
}
}
if !detected {
log.Print("No volume detected. Persistent messages may be lost")
} else {
return checkFS("/mnt/mqm")
}
return nil
}
func logConfig() error {
log.Printf("CPU architecture: %v", runtime.GOARCH)
if runtime.GOOS == "linux" {
var err error
osr, err := readProc("/proc/sys/kernel/osrelease")
if err != nil {
log.Print(err)
} else {
log.Printf("Linux kernel version: %v", osr)
}
logContainerRuntime()
logBaseImage()
fileMax, err := readProc("/proc/sys/fs/file-max")
if err != nil {
log.Print(err)
} else {
log.Printf("Maximum file handles: %v", fileMax)
}
logUser()
logCapabilities()
logSeccomp()
logSecurityAttributes()
err = readMounts()
if err != nil {
return err
}
} else {
return fmt.Errorf("Unsupported platform: %v", runtime.GOOS)
}
return nil
}

View File

@@ -0,0 +1,64 @@
/*
© Copyright IBM Corporation 2018
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 (
"fmt"
"os"
"path/filepath"
"strings"
"github.com/ibm-messaging/mq-container/internal/command"
)
// Verifies that we are the main or only instance of this program
func verifySingleProcess() error {
programName, err := determineExecutable()
if err != nil {
return fmt.Errorf("Failed to determine name of this program - %v", err)
}
// Verify that there is only one runmqserver
_, err = verifyOnlyOne(programName)
if err != nil {
return fmt.Errorf("You cannot run more than one instance of this program")
}
return nil
}
// Verifies that there is only one instance running of the given program name.
func verifyOnlyOne(programName string) (int, error) {
// #nosec G104
out, _, _ := command.Run("ps", "-e", "--format", "cmd")
//if this goes wrong then assume we are the only one
numOfProg := strings.Count(out, programName)
if numOfProg != 1 {
return numOfProg, fmt.Errorf("Expected there to be only 1 instance of %s but found %d", programName, numOfProg)
}
return numOfProg, nil
}
// Determines the name of the currently running executable.
func determineExecutable() (string, error) {
file, err := os.Executable()
if err != nil {
return "", err
}
_, exec := filepath.Split(file)
return exec, nil
}

View File

@@ -1,5 +1,5 @@
/*
© Copyright IBM Corporation 2017, 2018
© Copyright IBM Corporation 2017, 2019
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -16,11 +16,12 @@ limitations under the License.
package main
import (
"io"
"bytes"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"regexp"
"strings"
"github.com/ibm-messaging/mq-container/internal/command"
@@ -86,43 +87,35 @@ func configureQueueManager() error {
log.Println(err)
return err
}
for _, file := range files {
if strings.HasSuffix(file.Name(), ".mqsc") {
abs := filepath.Join(configDir, file.Name())
// #nosec G204
cmd := exec.Command("runmqsc")
stdin, err := cmd.StdinPipe()
// Read mqsc file into variable
mqsc, err := ioutil.ReadFile(abs)
if err != nil {
log.Println(err)
return err
log.Printf("Error reading file %v: %v", abs, err)
continue
}
// Open the MQSC file for reading
// #nosec G304
f, err := os.Open(abs)
// Write mqsc to buffer
var buffer bytes.Buffer
_, err = buffer.Write(mqsc)
if err != nil {
log.Printf("Error opening %v: %v", abs, err)
log.Printf("Error writing MQSC file %v to buffer: %v", abs, err)
continue
}
// Copy the contents to stdin of the runmqsc process
_, err = io.Copy(stdin, f)
if err != nil {
log.Errorf("Error reading %v: %v", abs, err)
}
err = f.Close()
if err != nil {
log.Errorf("Failed to close MQSC file handle: %v", err)
}
err = stdin.Close()
if err != nil {
log.Errorf("Failed to close MQSC stdin: %v", err)
}
// Run the command and wait for completion
// Buffer mqsc to stdin of runmqsc
cmd.Stdin = &buffer
// Run runmqsc command
out, err := cmd.CombinedOutput()
if err != nil {
log.Println(err)
}
log.Errorf("Error running MQSC file %v (%v):\n\t%v", file.Name(), err, formatMQSCOutput(string(out)))
continue
} else {
// Print the runmqsc output, adding tab characters to make it more readable as part of the log
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, formatMQSCOutput(string(out)))
}
}
}
return nil
@@ -130,7 +123,7 @@ func configureQueueManager() error {
func stopQueueManager(name string) error {
log.Println("Stopping queue manager")
out, _, err := command.Run("endmqm", "-w", name)
out, _, err := command.Run("endmqm", "-w", "-r", name)
if err != nil {
log.Printf("Error stopping queue manager: %v", string(out))
return err
@@ -138,3 +131,16 @@ func stopQueueManager(name string) error {
log.Println("Stopped queue manager")
return nil
}
func formatMQSCOutput(out string) string {
// redact sensitive information
pattern, _ := regexp.Compile("(?i)LDAPPWD\\s*?\\((.*?)\\)")
out = pattern.ReplaceAllString(out, "LDAPPWD(*********)")
pattern, _ = regexp.Compile("(?i)PASSWORD\\s*?\\((.*?)\\)")
out = pattern.ReplaceAllString(out, "PASSWORD(*********)")
pattern, _ = regexp.Compile("(?i)SSLCRYP\\s*?\\((.*?)\\)")
out = pattern.ReplaceAllString(out, "SSLCRYP(*********)")
// add tab characters to make it more readable as part of the log
return strings.Replace(string(out), "\n", "\n\t", -1)
}

View File

@@ -1,137 +0,0 @@
/*
© Copyright IBM Corporation 2018
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 (
"fmt"
"os/user"
"strings"
"github.com/ibm-messaging/mq-container/internal/command"
)
const groupName string = "supplgrp"
func verifyCurrentUser() error {
log.Debug("Verifying current user information")
curUser, err := user.Current()
if err != nil {
return err
}
log.Debugf("Detected current user as: %v+", curUser)
if curUser.Username == "mqm" {
// Not supported yet
return fmt.Errorf("Container is running as mqm user which is not supported. Please run this container as root")
} else if curUser.Username == "root" {
// We're running as root so need to check for supplementary groups.
// We can't use the golang User.GroupIDs as it doesn't seem to detect container supplementary groups..
groups, err := getCurrentUserGroups()
for _, e := range groups {
_, _, testGroup := command.Run("getent", "group", e)
if testGroup != nil {
log.Printf("Group %s does not exist on the system... Adding to system and MQM user", e)
_, _, err = command.Run("groupadd", "-g", e, groupName)
if err != nil {
log.Errorf("Failed to create group %s as %s", e, groupName)
return err
}
_, _, err = command.Run("usermod", "-aG", groupName, "mqm")
if err != nil {
log.Errorf("Failed to add group %s(%s) to the mqm user.", groupName, e)
return err
}
}
}
} else {
// We're running as an unknown user...
return fmt.Errorf("Container is running as %s user which is not supported. Please run this container as root", curUser.Username)
}
return nil
}
func logUser() {
u, usererr := user.Current()
if usererr == nil {
g, err := getCurrentUserGroups()
if err != nil && len(g) == 0 {
log.Printf("Running as user ID %v (%v) with primary group %v", u.Uid, u.Name, u.Gid)
} else {
// Look for the primary group in the list of group IDs
for i, v := range g {
if v == u.Gid {
// Remove the element from the slice
g = append(g[:i], g[i+1:]...)
}
}
log.Printf("Running as user ID %v (%v) with primary group %v, and supplementary groups %v", u.Uid, u.Name, u.Gid, strings.Join(g, ","))
}
}
if usererr == nil && u.Username != "mqm" {
mqm, err := user.Lookup("mqm")
// Need to print out mqm user details as well.
g, err := getUserGroups(mqm)
if err != nil && len(g) == 0 {
log.Printf("MQM user ID %v (%v) has primary group %v", mqm.Uid, "mqm", mqm.Gid)
} else {
// Look for the primary group in the list of group IDs
for i, v := range g {
if v == mqm.Gid {
// Remove the element from the slice
g = append(g[:i], g[i+1:]...)
}
}
log.Printf("MQM user ID %v (%v) has primary group %v, and supplementary groups %v", mqm.Uid, "mqm", mqm.Gid, strings.Join(g, ","))
}
}
}
func getCurrentUserGroups() ([]string, error) {
var nilArray []string
out, _, err := command.Run("id", "--groups")
if err != nil {
log.Debug("Unable to get current user groups")
return nilArray, err
}
out = strings.TrimSpace(out)
if out == "" {
// we don't have any groups?
return nilArray, fmt.Errorf("Unable to determine groups for current user")
}
groups := strings.Split(out, " ")
return groups, nil
}
func getUserGroups(usr *user.User) ([]string, error) {
var nilArray []string
out, _, err := command.Run("id", "--groups", usr.Uid)
if err != nil {
log.Debugf("Unable to get user %s groups", usr.Uid)
return nilArray, err
}
out = strings.TrimSpace(out)
if out == "" {
// we don't have any groups?
return nilArray, fmt.Errorf("Unable to determine groups for user %s", usr.Uid)
}
groups := strings.Split(out, " ")
return groups, nil
}

View File

@@ -29,6 +29,8 @@ var (
ImageRevision = "Not specified"
// ImageSource is the URL to get source code for building the image
ImageSource = "Not specified"
// ImageTag is the tag of the image
ImageTag = "Not specified"
)
func logDateStamp() {
@@ -43,6 +45,10 @@ func logGitCommit() {
log.Printf("Image source: %v", ImageSource)
}
func logImageTag() {
log.Printf("Image tag: %v", ImageTag)
}
func logMQVersion() {
mqVersion, _, err := command.Run("dspmqver", "-b", "-f", "2")
if err != nil {
@@ -67,5 +73,6 @@ func logVersionInfo() {
logDateStamp()
logGitRepo()
logGitCommit()
logImageTag()
logMQVersion()
}

View File

@@ -1,7 +1,7 @@
// +build mqdev
/*
© Copyright IBM Corporation 2018
© Copyright IBM Corporation 2018, 2019
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -22,7 +22,9 @@ import (
"io"
"os"
"os/exec"
"os/user"
"path/filepath"
"strconv"
"syscall"
"github.com/ibm-messaging/mq-container/internal/command"
@@ -42,12 +44,23 @@ func startWebServer() error {
// Take all current environment variables, and add the app password
cmd.Env = append(os.Environ(), "MQ_APP_PASSWORD=passw0rd")
}
cmd.SysProcAttr = &syscall.SysProcAttr{}
uid, gid, err := command.LookupMQM()
if err != nil {
return err
}
u, err := user.Current()
if err != nil {
return err
}
currentUID, err := strconv.Atoi(u.Uid)
if err != nil {
return fmt.Errorf("Error converting UID to string: %v", err)
}
// Add credentials to run as 'mqm', only if we aren't already 'mqm'
if currentUID != uid {
cmd.SysProcAttr = &syscall.SysProcAttr{}
cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)}
}
out, rc, err := command.RunCmd(cmd)
if err != nil {
log.Printf("Error %v starting web server: %v", rc, string(out))

39
docs/security.md Normal file
View File

@@ -0,0 +1,39 @@
# Security
## Container runtime
### User
The MQ server image is run using the "mqm" user. On the Ubuntu-based image, this uses the UID and GID of 999. On the Red Hat Enterprise Linux image, it uses the UID and GID of 888.
### Capabilities
The MQ Advanced image requires no Linux capabilities, so you can drop any capabilities which are added by default. For example, in Docker you could do the following:
```sh
docker run \
--cap-drop=ALL \
--env LICENSE=accept \
--env MQ_QMGR_NAME=QM1 \
--detach \
mqadvanced-server:9.1.2.0-x86_64-ubuntu-16.04
```
The MQ Advanced for Developers image does require the "chown", "setuid", "setgid" and "audit_write" capabilities (plus "dac_override" if you're using an image based on Red Hat Enterprise Linux). This is because it uses the "sudo" command to change passwords inside the container. For example, in Docker, you could do the following:
```sh
docker run \
--cap-drop=ALL \
--cap-add=CHOWN \
--cap-add=SETUID \
--cap-add=SETGID \
--cap-add=AUDIT_WRITE \
--env LICENSE=accept \
--env MQ_QMGR_NAME=QM1 \
--detach \
mqadvanced-server-dev:9.1.2.0-x86_64-ubuntu-16.04
```
### SELinux
The SELinux label "spc_t" (super-privileged container) is needed to run the MQ container on a host with SELinux enabled. This is due to a current limitation in how MQ data is stored on volumes, which violates the usual policy applied when using the standard "container_t" label.

View File

@@ -8,6 +8,12 @@ You need to ensure you have the following tools installed:
* [dep](https://github.com/golang/dep) (official Go dependency management tool) - needed to prepare for running the tests
* [Helm](https://helm.sh) - only needed for running the Kubernetes tests
### Prerequisites for testing a RedHat image
If you want to test a container image with Red Hat Enterprise Linux as the base OS, then you need to use a host server with Red Hat Enterprise Linux. You must also have the following tools installed:
* [Yum](http://yum.baseurl.org/) (available in `rhel-7-server-extras`)
* [Buildah](https://buildah.io) (available in `rhel-7-server-extras`)
## Running the tests
There are two main sets of tests:
@@ -25,7 +31,7 @@ make test-advancedserver
You can specify the image to use directly by using the `MQ_IMAGE_ADVANCEDSERVER` or `MQ_IMAGE_DEVSERVER` variables, for example:
```
MQ_IMAGE_ADVANCEDSERVER=mqadvanced-server:9.1.1.0-x86_64-ubuntu-16.04 make test-advancedserver
MQ_IMAGE_ADVANCEDSERVER=mqadvanced-server:9.1.2.0-x86_64-ubuntu-16.04 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::
@@ -34,10 +40,10 @@ You can pass parameters to `go test` with an environment variable. For example,
TEST_OPTS_DOCKER="-run TestGoldenPath" make test-advancedserver
```
You can also use the same environment variables you specified when [building](./building), for example, the following will try and test an image called `mqadvanced-server:9.1.0.0-x86_64-ubuntu-16.04`:
You can also use the same environment variables you specified when [building](./building), for example, the following will try and test an image called `mqadvanced-server:9.1.2.0-x86_64-ubuntu-16.04`:
```
MQ_VERSION=9.1.0.0 make test-advancedserver
MQ_VERSION=9.1.2.0 make test-advancedserver
```
### Running the Docker tests with code coverage

View File

@@ -66,20 +66,20 @@ The following is an *example* `Dockerfile` for creating your own pre-configured
```dockerfile
FROM ibmcom/mq
USER root
RUN useradd alice -G mqm && \
echo alice:passw0rd | chpasswd
USER mqm
COPY 20-config.mqsc /etc/mqm/
```
Here is an example corresponding `20-config.mqsc` script from the [mqdev blog](https://developer.ibm.com/messaging/2018/10/01/archives-getting-going-without-turning-off-ibm-mq-security/), which allows users with passwords to connect on the `PASSWORD.SVRCONN` channel:
The `USER` instructions are necessary to ensure that the `useradd` and `chpasswd` commands are run as the root user.
Here is an example corresponding `20-config.mqsc` script, which creates two local queues:
```mqsc
DEFINE CHANNEL(PASSWORD.SVRCONN) CHLTYPE(SVRCONN) REPLACE
SET CHLAUTH(PASSWORD.SVRCONN) TYPE(BLOCKUSER) USERLIST('nobody') DESCR('Allow privileged users on this channel')
SET CHLAUTH('*') TYPE(ADDRESSMAP) ADDRESS('*') USERSRC(NOACCESS) DESCR('BackStop rule')
SET CHLAUTH(PASSWORD.SVRCONN) TYPE(ADDRESSMAP) ADDRESS('*') USERSRC(CHANNEL) CHCKCLNT(REQUIRED)
ALTER AUTHINFO(SYSTEM.DEFAULT.AUTHINFO.IDPWOS) AUTHTYPE(IDPWOS) ADOPTCTX(YES)
REFRESH SECURITY TYPE(CONNAUTH)
DEFINE QLOCAL(MY.QUEUE.1) REPLACE
DEFINE QLOCAL(MY.QUEUE.2) REPLACE
```
The file `20-config.mqsc` should be saved into the same directory as the `Dockerfile`.

View File

@@ -1,4 +1,4 @@
# © Copyright IBM Corporation 2015, 2017
# © Copyright IBM Corporation 2015, 2019
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -20,9 +20,11 @@ ARG MQ_URL=https://public.dhe.ibm.com/ibmdl/export/pub/software/websphere/messag
# The MQ packages to install
ARG MQ_PACKAGES="ibmmq-sfbridge"
ARG MQM_UID=999
ADD install-mq.sh /usr/local/bin/
RUN chmod u+x /usr/local/bin/install-mq.sh \
&& install-mq.sh
&& install-mq.sh $MQM_UID
ENV LANG=en_US.UTF-8

View File

@@ -1,4 +1,4 @@
# © Copyright IBM Corporation 2015, 2017
# © Copyright IBM Corporation 2015, 2019
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -18,7 +18,9 @@ FROM ubuntu:16.04
ARG MQ_URL=https://public.dhe.ibm.com/ibmdl/export/pub/software/websphere/messaging/mqadv/mqadv_dev911_ubuntu_x86-64.tar.gz
# The MQ packages to install
ARG MQ_PACKAGES="ibmmq-explorer"
ARG MQ_PACKAGES
ARG MQM_UID=999
RUN export DEBIAN_FRONTEND=noninteractive \
&& apt-get update \

View File

@@ -1,4 +1,4 @@
# © Copyright IBM Corporation 2018
# © Copyright IBM Corporation 2018, 2019
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.

View File

@@ -1,4 +1,4 @@
# © Copyright IBM Corporation 2018
# © Copyright IBM Corporation 2018, 2019
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -23,6 +23,8 @@ ARG MQ_URL
# The packages to install in install-mq.sh
ARG MQ_PACKAGES
ARG MQM_UID=999
COPY install-mq.sh /usr/local/bin/
# Install MQ. To avoid a "text file busy" error here, we sleep before installing.
@@ -30,6 +32,6 @@ COPY install-mq.sh /usr/local/bin/
# errors with some commands (e.g. `dspmqver`)
RUN chmod u+x /usr/local/bin/install-mq.sh \
&& sleep 1 \
&& install-mq.sh \
&& install-mq.sh $MQM_UID \
&& rm -rf /var/mqm \
&& /opt/mqm/bin/crtmqdir -f -s

View File

@@ -1,4 +1,4 @@
* © Copyright IBM Corporation 2017, 2018
* © Copyright IBM Corporation 2017, 2019
*
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
STOP LISTENER('SYSTEM.LISTENER.TCP.1')
STOP LISTENER('SYSTEM.LISTENER.TCP.1') IGNSTATE(YES)
* Developer queues
DEFINE QLOCAL('DEV.QUEUE.1') REPLACE
@@ -50,4 +50,4 @@ SET AUTHREC PROFILE('DEV.**') GROUP('mqclient') OBJTYPE(TOPIC) AUTHADD(PUB,SUB)
* Developer listener
DEFINE LISTENER('DEV.LISTENER.TCP') TRPTYPE(TCP) PORT(1414) CONTROL(QMGR) REPLACE
START LISTENER('DEV.LISTENER.TCP')
START LISTENER('DEV.LISTENER.TCP') IGNSTATE(YES)

View File

@@ -1,4 +1,4 @@
# © Copyright IBM Corporation 2015, 2018
# © Copyright IBM Corporation 2015, 2019
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -12,8 +12,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
ARG BASE_IMAGE=mqadvanced-server-dev-base:9.1.1.0-x86_64-ubuntu-16.04
ARG BUILDER_IMAGE=mq-golang-sdk:9.1.1.0-x86_64-ubuntu-16.04
ARG BASE_IMAGE=mqadvanced-server-dev-base:9.1.2.0-x86_64-ubuntu-16.04
ARG BUILDER_IMAGE=mq-golang-sdk:9.1.2.0-x86_64-ubuntu-16.04
###############################################################################
# Build stage to build Go code
@@ -21,12 +21,13 @@ ARG BUILDER_IMAGE=mq-golang-sdk:9.1.1.0-x86_64-ubuntu-16.04
FROM $BUILDER_IMAGE as builder
ARG IMAGE_REVISION="Not specified"
ARG IMAGE_SOURCE="Not specified"
ARG IMAGE_TAG="Not specified"
WORKDIR /go/src/github.com/ibm-messaging/mq-container/
COPY cmd/ ./cmd
COPY internal/ ./internal
COPY vendor/ ./vendor
# Re-build runmqserver, with code tagged with 'mqdev' enabled
RUN go build -ldflags "-X \"main.ImageCreated=$(date --iso-8601=seconds)\" -X \"main.ImageRevision=$IMAGE_REVISION\" -X \"main.ImageSource=$IMAGE_SOURCE\"" --tags 'mqdev' ./cmd/runmqserver
RUN go build -ldflags "-X \"main.ImageCreated=$(date --iso-8601=seconds)\" -X \"main.ImageRevision=$IMAGE_REVISION\" -X \"main.ImageSource=$IMAGE_SOURCE\" -X \"main.ImageTag=$IMAGE_TAG\"" --tags 'mqdev' ./cmd/runmqserver
RUN go build ./cmd/runmqdevserver/
# Run all unit tests
RUN go test -v ./cmd/runmqdevserver/...
@@ -42,6 +43,20 @@ ENV MQ_DEV=true
# Default administrator password
ENV MQ_ADMIN_PASSWORD=passw0rd
ARG MQM_UID=999
USER root
COPY incubating/mqadvanced-server-dev/install-extra-packages.sh /usr/local/bin/
RUN chmod u+x /usr/local/bin/install-extra-packages.sh \
&& sleep 1 \
&& install-extra-packages.sh
# WARNING: This is what allows the mqm user to change the password of any other user
# It's used by runmqdevserver to change the admin/app passwords.
RUN echo "mqm ALL = NOPASSWD: /usr/sbin/chpasswd" > /etc/sudoers.d/mq-dev-config
## Add admin and app users, and set a default password for admin
RUN useradd admin -G mqm \
&& groupadd mqclient \
@@ -54,12 +69,18 @@ RUN mkdir -p /run/runmqdevserver \
COPY --from=builder /go/src/github.com/ibm-messaging/mq-container/runmqserver /usr/local/bin/
COPY --from=builder /go/src/github.com/ibm-messaging/mq-container/runmqdevserver /usr/local/bin/
# Copy template files
COPY incubating/mqadvanced-server-dev/*.tpl /etc/mqm/
# Copy web XML files for default developer configuration
COPY incubating/mqadvanced-server-dev/web /etc/mqm/web
RUN chmod +x /usr/local/bin/runmq*
RUN chown -R mqm:mqm /etc/mqm/* \
&& chmod +x /usr/local/bin/runmq* \
&& install --directory --mode 0775 --owner mqm --group root /run/runmqdevserver
EXPOSE 9443
USER $MQM_UID
ENTRYPOINT ["runmqdevserver"]

View File

@@ -0,0 +1,32 @@
#!/bin/bash
# -*- mode: sh -*-
# © Copyright IBM Corporation 2019
#
#
# 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.
test -f /usr/bin/yum && RHEL=true || RHEL=false
test -f /usr/bin/apt-get && UBUNTU=true || UBUNTU=false
if ($UBUNTU); then
export DEBIAN_FRONTEND=noninteractive
apt-get update
apt-get install -y --no-install-recommends sudo
rm -rf /var/lib/apt/lists/*
fi
if ($RHEL); then
yum -y install sudo
yum -y clean all
rm -rf /var/cache/yum/*
fi

View File

@@ -1,6 +1,6 @@
#!/bin/bash
# -*- mode: sh -*-
# © Copyright IBM Corporation 2015, 2018
# © Copyright IBM Corporation 2015, 2019
#
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,7 +20,7 @@
set -ex
curl https://glide.sh/get | sh
sudo curl -Lo /usr/local/bin/dep https://github.com/golang/dep/releases/download/v0.4.1/dep-linux-amd64
sudo curl -Lo /usr/local/bin/dep https://github.com/golang/dep/releases/download/v0.5.0/dep-linux-amd64
sudo chmod +x /usr/local/bin/dep
go get golang.org/x/lint/golint
go get -u golang.org/x/lint/golint

View File

@@ -1,6 +1,6 @@
#!/bin/bash
# -*- mode: sh -*-
# © Copyright IBM Corporation 2015, 2018
# © Copyright IBM Corporation 2015, 2019
#
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,6 +18,8 @@
# Fail on any non-zero return code
set -ex
mqm_uid=${1:-999}
test -f /usr/bin/yum && RHEL=true || RHEL=false
test -f /usr/bin/apt-get && UBUNTU=true || UBUNTU=false
@@ -102,11 +104,8 @@ $UBUNTU && apt-get purge -y \
$UBUNTU && apt-get autoremove -y
# Recommended: Create the mqm user ID with a fixed UID and group, so that the file permissions work between different images
$UBUNTU && groupadd --system --gid 999 mqm
$UBUNTU && useradd --system --uid 999 --gid mqm mqm
$RHEL && groupadd --system --gid 888 mqm
$RHEL && useradd --system --uid 888 --gid mqm mqm
usermod -aG mqm root
groupadd --system --gid ${mqm_uid} mqm
useradd --system --uid ${mqm_uid} --gid mqm --groups 0 mqm
# Find directory containing .deb files
$UBUNTU && DIR_DEB=$(find ${DIR_EXTRACT} -name "*.deb" -printf "%h\n" | sort -u | head -1)
@@ -139,7 +138,7 @@ rm -rf ${DIR_EXTRACT}
# 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
$UBUNTU && apt-get install -y libapparmor1 libsystemd0 systemd systemd-sysv libudev1 --only-upgrade
$UBUNTU && apt-get install -y libapparmor1 libsystemd0 systemd systemd-sysv libudev1 perl-base --only-upgrade
# End of bug fixes
# Clean up cached files
@@ -153,16 +152,18 @@ $UBUNTU && echo "mq:$(dspmqver -b -f 2)" > /etc/debian_chroot
# Remove the directory structure under /var/mqm which was created by the installer
rm -rf /var/mqm
# Create the mount point for volumes
mkdir -p /mnt/mqm
# Create the mount point for volumes, ensuring MQ has permissions to all directories
install --directory --mode 0775 --owner mqm --group root /mnt
install --directory --mode 0775 --owner mqm --group root /mnt/mqm
install --directory --mode 0775 --owner mqm --group root /mnt/mqm/data
# Create the directory for MQ configuration files
mkdir -p /etc/mqm
install --directory --mode 0775 --owner mqm --group root /etc/mqm
# Create a symlink for /var/mqm -> /mnt/mqm/data
ln -s /mnt/mqm/data /var/mqm
# Optional: Set these values for the Bluemix Vulnerability Report
# Optional: Ensure any passwords expire in a timely manner
sed -i 's/PASS_MAX_DAYS\t99999/PASS_MAX_DAYS\t90/' /etc/login.defs
sed -i 's/PASS_MIN_DAYS\t0/PASS_MIN_DAYS\t1/' /etc/login.defs

View File

@@ -0,0 +1,109 @@
/*
© Copyright IBM Corporation 2019
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 runtime
import (
"fmt"
"io/ioutil"
"strings"
"github.com/genuinetools/amicontained/container"
)
func GetContainerRuntime() (string, error) {
return container.DetectRuntime()
}
func GetBaseImage() (string, error) {
buf, err := ioutil.ReadFile("/etc/os-release")
if err != nil {
return "", fmt.Errorf("Failed to read /etc/os-release: %v", err)
}
lines := strings.Split(string(buf), "\n")
for _, l := range lines {
if strings.HasPrefix(l, "PRETTY_NAME=") {
words := strings.Split(l, "\"")
if len(words) >= 2 {
return words[1], nil
}
}
}
return "unknown", nil
}
// GetCapabilities gets the Linux capabilities (e.g. setuid, setgid). See https://docs.docker.com/engine/reference/run/#runtime-privilege-and-linux-capabilities
func GetCapabilities() (map[string][]string, error) {
return container.Capabilities()
}
// GetSeccomp gets the seccomp enforcing mode, which affects which kernel calls can be made
func GetSeccomp() (string, error) {
s, err := container.SeccompEnforcingMode()
if err != nil {
return "", fmt.Errorf("Failed to get container SeccompEnforcingMode: %v", err)
}
return s, nil
}
// GetSecurityAttributes gets the security attributes of the current process.
// The security attributes indicate whether AppArmor or SELinux are being used,
// and what the level of confinement is.
func GetSecurityAttributes() string {
a, err := readProc("/proc/self/attr/current")
// On some systems, if AppArmor or SELinux are not installed, you get an
// error when you try and read `/proc/self/attr/current`, even though the
// file exists.
if err != nil || a == "" {
a = "none"
}
return a
}
func readProc(filename string) (value string, err error) {
// #nosec G304
buf, err := ioutil.ReadFile(filename)
if err != nil {
return "", err
}
return strings.TrimSpace(string(buf)), nil
}
func GetMounts() (map[string]string, error) {
all, err := readProc("/proc/mounts")
if err != nil {
return nil, fmt.Errorf("Couldn't read /proc/mounts")
}
result := make(map[string]string)
lines := strings.Split(all, "\n")
for i := range lines {
parts := strings.Split(lines[i], " ")
//dev := parts[0]
mountPoint := parts[1]
fsType := parts[2]
if strings.Contains(mountPoint, "/mnt/mqm") {
result[mountPoint] = fsType
}
}
return result, nil
}
func GetKernelVersion() (string, error) {
return readProc("/proc/sys/kernel/osrelease")
}
func GetMaxFileHandles() (string, error) {
return readProc("/proc/sys/fs/file-max")
}

View File

@@ -1,7 +1,7 @@
// +build linux
/*
© Copyright IBM Corporation 2017, 2018
© Copyright IBM Corporation 2017, 2019
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -15,11 +15,9 @@ 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
package runtime
import (
"fmt"
"golang.org/x/sys/unix"
)
@@ -101,24 +99,27 @@ var fsTypes = map[int64]string{
0x58295829: "zsmalloc",
}
func checkFS(path string) error {
// GetFilesystem returns the filesystem type for the specified path
func GetFilesystem(path string) (string, error) {
statfs := &unix.Statfs_t{}
err := unix.Statfs(path, statfs)
if err != nil {
log.Println(err)
return nil
return "", err
}
// Use a type conversion to make type an int64. On s390x it's a uint32.
t, ok := fsTypes[int64(statfs.Type)]
if !ok {
log.Printf("WARNING: detected %v has unknown filesystem type %x", path, statfs.Type)
return nil
return "unknown", nil
}
switch t {
return t, nil
}
// SupportedFilesystem returns true if the supplied filesystem type is supported for MQ data
func SupportedFilesystem(fsType string) bool {
switch fsType {
case "aufs", "overlayfs", "tmpfs":
return fmt.Errorf("%v uses unsupported filesystem type: %v", path, t)
return false
default:
log.Printf("Detected %v has filesystem type '%v'", path, t)
return nil
return true
}
}

View File

@@ -1,7 +1,7 @@
// +build !linux
/*
© Copyright IBM Corporation 2018
© Copyright IBM Corporation 2018, 2019
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -15,7 +15,7 @@ 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
package runtime
// Dummy version of this function, only for non-Linux systems.
// Having this allows unit tests to be run on other platforms (e.g. macOS)

View File

@@ -1,5 +1,5 @@
/*
© Copyright IBM Corporation 2018
© Copyright IBM Corporation 2018, 2019
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -44,7 +44,7 @@ type Logger struct {
pid string
serverName string
host string
user *user.User
userName string
}
// NewLogger creates a new logger
@@ -53,9 +53,13 @@ func NewLogger(writer io.Writer, debug bool, json bool, serverName string) (*Log
if err != nil {
return nil, err
}
// This can fail because the container's running as a random UID which
// is not known by the OS. We don't want this to break the logging
// entirely, so just use a blank user name.
user, err := user.Current()
if err != nil {
return nil, err
userName := ""
if err == nil {
userName = user.Username
}
return &Logger{
mutex: sync.Mutex{},
@@ -66,7 +70,7 @@ func NewLogger(writer io.Writer, debug bool, json bool, serverName string) (*Log
pid: strconv.Itoa(os.Getpid()),
serverName: serverName,
host: hostname,
user: user,
userName: userName,
}, nil
}
@@ -93,7 +97,7 @@ func (l *Logger) log(level string, msg string) {
"ibm_serverName": l.serverName,
"ibm_processName": l.processName,
"ibm_processId": l.pid,
"ibm_userName": l.user.Username,
"ibm_userName": l.userName,
"type": "mq_containerlog",
}
s, err := l.format(entry)

81
internal/user/user.go Normal file
View File

@@ -0,0 +1,81 @@
/*
© Copyright IBM Corporation 2018, 2019
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 user
import (
"fmt"
"os/user"
"strings"
"github.com/ibm-messaging/mq-container/internal/command"
)
// User holds information on primary and supplemental OS groups
type User struct {
UID string
Name string
PrimaryGID string
SupplementalGID []string
}
// GetUser returns the current user and group information
func GetUser() (User, error) {
u, err := user.Current()
if err != nil {
return User{}, err
}
g, err := getCurrentUserGroups()
if err != nil {
return User{}, err
}
if err != nil && len(g) == 0 {
return User{
UID: u.Uid,
Name: u.Name,
PrimaryGID: u.Gid,
SupplementalGID: []string{},
}, nil
}
// Look for the primary group in the list of group IDs
for i, v := range g {
if v == u.Gid {
// Remove the element from the slice
g = append(g[:i], g[i+1:]...)
}
}
return User{
UID: u.Uid,
Name: u.Name,
PrimaryGID: u.Gid,
SupplementalGID: g,
}, nil
}
func getCurrentUserGroups() ([]string, error) {
var nilArray []string
out, _, err := command.Run("id", "--groups")
if err != nil {
return nilArray, err
}
out = strings.TrimSpace(out)
if out == "" {
return nilArray, fmt.Errorf("Unable to determine groups for current user")
}
groups := strings.Split(out, " ")
return groups, nil
}

View File

@@ -1,4 +1,4 @@
# © Copyright IBM Corporation 2018
# © Copyright IBM Corporation 2018, 2019
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -12,17 +12,17 @@
# See the License for the specific language governing permissions and
# limitations under the License.
image: ibmcom/mq:9
image: ibmcom/mq:9.1.2.0
manifests:
- image: ibmcom/mq:9.1.1.0-x86_64
- image: ibmcom/mq:9.1.2.0-x86_64
platform:
architecture: amd64
os: linux
- image: ibmcom/mq:9.1.1.0-ppc64le
- image: ibmcom/mq:9.1.2.0-ppc64le
platform:
architecture: ppc64le
os: linux
- image: ibmcom/mq:9.1.1.0-s390x
- image: ibmcom/mq:9.1.2.0-s390x
platform:
architecture: s390x
os: linux

View File

@@ -1,4 +1,4 @@
# © Copyright IBM Corporation 2018
# © Copyright IBM Corporation 2018, 2019
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -14,15 +14,15 @@
image: ibmcom/mq:latest
manifests:
- image: ibmcom/mq:9.1.1.0-x86_64
- image: ibmcom/mq:9.1.2.0-x86_64
platform:
architecture: amd64
os: linux
- image: ibmcom/mq:9.1.1.0-ppc64le
- image: ibmcom/mq:9.1.2.0-ppc64le
platform:
architecture: ppc64le
os: linux
- image: ibmcom/mq:9.1.1.0-s390x
- image: ibmcom/mq:9.1.2.0-s390x
platform:
architecture: s390x
os: linux

View File

@@ -0,0 +1,29 @@
# © Copyright IBM Corporation 2018, 2019
#
# 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.
image: ibmcorp/mqadvanced-server-dev:9.1.2.0
manifests:
- image: ibmcorp/mqadvanced-server-dev:9.1.2.0-x86_64
platform:
architecture: amd64
os: linux
- image: ibmcorp/mqadvanced-server-dev:9.1.2.0-ppc64le
platform:
architecture: ppc64le
os: linux
- image: ibmcorp/mqadvanced-server-dev:9.1.2.0-s390x
platform:
architecture: s390x
os: linux

View File

@@ -1,7 +1,3 @@
This is a work-in-progress for a Docker image based on Red Hat Enterprise Linux (RHEL).
# RHEL-based container build
The current MQ container build requires Docker V17.05 or greater (required features include multi-stage Docker build, and "ARG"s in the "FROM" statement). Red Hat Enterprise Linux V7.5 includes Docker up to version V1.13.
In order to build images with Red Hat Enterprise Linux, license registration is required. The license of the host server can be used, as long as you either use Red Hat's patched version of Docker (which is an old version), or if you use alternative container management tools such as [`buildah`](https://github.com/projectatomic/buildah/) and `podman` (from [`libpod`](https://github.com/projectatomic/libpod)).
This directory contains scripts for building with `buildah`. The build itself isn't containerized, so more software than usual is needed on the RHEL host, so an Ansible playbook is also provided to help set up the host.
Build scripts for building a container image based on Red Hat Enterprise Linux (RHEL), using the [`buildah`](https://github.com/containers/buildah) tool. buildah is supported on RHEL V7.5 and greater.

View File

@@ -1,6 +1,6 @@
#!/bin/bash
# -*- mode: sh -*-
# © Copyright IBM Corporation 2018
# © Copyright IBM Corporation 2018, 2019
#
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,9 +17,11 @@
# Builds and tests the golang programs used by the MQ image.
set -e
set -ex
cd $GOPATH/src/github.com/ibm-messaging/mq-container/
# Handle a GOPATH with multiple entries (just choose the first one)
IFS=':' read -ra DIR <<< "$GOPATH"
cd ${DIR[0]}/src/github.com/ibm-messaging/mq-container/
# Build and test the Go code
mkdir -p build

View File

@@ -1,6 +1,6 @@
#!/bin/bash
# -*- mode: sh -*-
# © Copyright IBM Corporation 2018
# © Copyright IBM Corporation 2018, 2019
#
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -34,12 +34,16 @@ readonly dev=$2
IMAGE_REVISION=${IMAGE_REVISION:="Not Applicable"}
IMAGE_SOURCE=${IMAGE_SOURCE:="Not Applicable"}
# Run the build in a container
# Note the ":Z" on the volume is to allow the container to access the files when SELinux is enabled
# Note the "podman" network is used explicitly, to avoid problems other CNI networks (e.g. on an OpenShift node)
podman run \
--volume ${PWD}:/go/src/github.com/ibm-messaging/mq-container/ \
--env GOPATH=/go \
--volume ${PWD}:/opt/app-root/src/go/src/github.com/ibm-messaging/mq-container/:Z \
--env IMAGE_REVISION="$IMAGE_REVISION" \
--env IMAGE_SOURCE="$IMAGE_SOURCE" \
--env MQDEV=${dev} \
--user $(id -u) \
--rm \
--network podman \
${tag} \
bash -c "cd /go/src/github.com/ibm-messaging/mq-container/ && ./mq-advanced-server-rhel/go-build.sh"
bash -c "cd /opt/app-root/src/go/src/github.com/ibm-messaging/mq-container/ && ./mq-advanced-server-rhel/go-build.sh"

View File

@@ -1,6 +1,6 @@
#!/bin/bash
# -*- mode: sh -*-
# © Copyright IBM Corporation 2018
# © Copyright IBM Corporation 2018, 2019
#
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -34,6 +34,8 @@ readonly mnt_mq=$2
readonly archive=$3
readonly mq_packages=$4
readonly dir_extract=/tmp/extract
readonly mqm_uid=888
readonly mqm_gid=888
if [ ! -d ${dir_extract}/MQServer ]; then
mkdir -p ${dir_extract}
@@ -42,13 +44,11 @@ if [ ! -d ${dir_extract}/MQServer ]; then
echo Extracting finished
fi
# If MQ_PACKAGES isn't specifically set, then choose a valid set of defaults
# Accept the MQ license
buildah run --volume ${dir_extract}:/mnt/mq-download $ctr_mq -- /mnt/mq-download/MQServer/mqlicense.sh -text_only -accept
buildah run --user root --volume ${dir_extract}:/mnt/mq-download:Z $ctr_mq -- /mnt/mq-download/MQServer/mqlicense.sh -text_only -accept
buildah run --volume ${dir_extract}:/mnt/mq-download $ctr_mq -- bash -c "cd /mnt/mq-download/MQServer && rpm -ivh $mq_packages"
# Install MQ
buildah run --user root --volume ${dir_extract}:/mnt/mq-download:Z $ctr_mq -- bash -c "cd /mnt/mq-download/MQServer && rpm -ivh $mq_packages"
rm -rf ${dir_extract}/MQServer
@@ -62,16 +62,23 @@ find $mnt_mq/opt/mqm -name '*.tar.gz' -delete
buildah run $ctr_mq -- /opt/mqm/bin/setmqinst -p /opt/mqm -i
mkdir -p $mnt_mq/run/runmqserver
chown 888:888 $mnt_mq/run/runmqserver
chown ${mqm_uid}:${mqm_gid} $mnt_mq/run/runmqserver
# Remove the directory structure under /var/mqm which was created by the installer
rm -rf $mnt_mq/var/mqm
# Create the mount point for volumes
# Create the mount point for volumes, ensuring MQ has permissions to all directories
mkdir -p $mnt_mq/mnt/mqm
install --directory --mode 0775 --owner ${mqm_uid} --group root $mnt_mq/mnt
install --directory --mode 0775 --owner ${mqm_uid} --group root $mnt_mq/mnt/mqm
install --directory --mode 0775 --owner ${mqm_uid} --group root $mnt_mq/mnt/mqm/data
# Create the directory for MQ configuration files
mkdir -p /etc/mqm
install --directory --mode 0775 --owner ${mqm_uid} --group root $mnt_mq/etc/mqm
# Create a symlink for /var/mqm -> /mnt/mqm/data
buildah run $ctr_mq -- ln -s /mnt/mqm/data /var/mqm
buildah run --user root $ctr_mq -- ln -s /mnt/mqm/data /var/mqm
# Optional: Set these values for the IBM Cloud Vulnerability Report
sed -i 's/PASS_MAX_DAYS\t99999/PASS_MAX_DAYS\t90/' $mnt_mq/etc/login.defs

View File

@@ -1,6 +1,6 @@
#!/bin/bash
# -*- mode: sh -*-
# © Copyright IBM Corporation 2018
# © Copyright IBM Corporation 2018, 2019
#
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,14 +16,12 @@
# limitations under the License.
# Build a RHEL image, using the buildah tool
# Usage
# mq-buildah.sh ARCHIVEFILE PACKAGES
set -x
set -e
function usage {
echo "Usage: $0 ARCHIVENAME PACKAGES TAG VERSION MQDevFlag"
echo "Usage: $0 ARCHIVE-NAME PACKAGES TAG VERSION MQDevFlag"
exit 20
}
@@ -36,7 +34,8 @@ fi
# Setup MQ server working container
###############################################################################
readonly ctr_mq=$(buildah from rhel7)
# Use RHEL 7 minimal container (which doesn't include things like Python or Yum)
readonly ctr_mq=$(buildah from rhel7-minimal)
if [ -z "$ctr_mq" ]
then
echo "ERROR: ctr_mq is empty. Check above output for errors"
@@ -55,18 +54,24 @@ readonly packages=$2
readonly tag=$3
readonly version=$4
readonly mqdev=$5
readonly mqm_uid=888
readonly mqm_gid=888
###############################################################################
# Install MQ server
###############################################################################
groupadd --root ${mnt_mq} --system --gid 888 mqm
useradd --root ${mnt_mq} --system --uid 888 --gid mqm mqm
usermod --root ${mnt_mq} -aG root mqm
usermod --root ${mnt_mq} -aG mqm root
# Install the packages required by MQ
buildah run $ctr_mq -- yum install -y --setopt install_weak_deps=false --setopt=tsflags=nodocs --setopt=override_install_langs=en_US.utf8 \
microdnf_opts="--nodocs"
# Check whether the host is registered with Red Hat
if subscription-manager status ; then
# Host is subscribed, but the minimal image has no enabled repos
# Note that the "bc" package is the only one in "extras"
microdnf_opts="${microdnf_opts} --enablerepo=rhel-7-server-rpms --enablerepo=rhel-7-server-extras-rpms"
else
# Use the Yum repositories configured on the host
cp -R /etc/yum.repos.d/* ${mnt_mq}/etc/yum.repos.d/
fi
buildah run ${ctr_mq} -- microdnf ${microdnf_opts} install \
bash \
bc \
coreutils \
@@ -78,24 +83,43 @@ buildah run $ctr_mq -- yum install -y --setopt install_weak_deps=false --setopt=
passwd \
procps-ng \
sed \
shadow-utils \
tar \
util-linux
util-linux \
which
# Install "sudo" if using MQ Advanced for Developers
if [ "$mqdev" = "TRUE" ]; then
buildah run ${ctr_mq} -- microdnf ${microdnf_opts} install sudo
fi
# Clean up cached files
buildah run $ctr_mq -- yum clean all
rm -rf ${mnt_mq}/var/cache/yum/*
buildah run ${ctr_mq} -- microdnf ${microdnf_opts} clean all
rm -rf ${mnt_mq}/etc/yum.repos.d/*
buildah run --user root $ctr_mq -- groupadd --system --gid ${mqm_gid} mqm
buildah run --user root $ctr_mq -- useradd --system --uid ${mqm_uid} --gid mqm --groups 0 mqm
# Install MQ server packages into the MQ builder image
./mq-advanced-server-rhel/install-mq-rhel.sh ${ctr_mq} "${mnt_mq}" "${archive}" "${packages}"
# Create the directory for MQ configuration files
mkdir -p ${mnt_mq}/etc/mqm
chown 888:888 ${mnt_mq}/etc/mqm
chown ${mqm_uid}:${mqm_gid} ${mnt_mq}/etc/mqm
# Install the Go binaries into the image
install --mode 0750 --owner 888 --group 888 ./build/runmqserver ${mnt_mq}/usr/local/bin/
install --mode 6750 --owner 888 --group 888 ./build/chk* ${mnt_mq}/usr/local/bin/
install --mode 0750 --owner 888 --group 888 ./NOTICES.txt ${mnt_mq}/opt/mqm/licenses/notices-container.txt
install --mode 0750 --owner ${mqm_uid} --group 0 ./build/runmqserver ${mnt_mq}/usr/local/bin/
install --mode 6750 --owner ${mqm_uid} --group 0 ./build/chk* ${mnt_mq}/usr/local/bin/
install --mode 0750 --owner ${mqm_uid} --group 0 ./NOTICES.txt ${mnt_mq}/opt/mqm/licenses/notices-container.txt
install --directory --mode 0775 --owner ${mqm_uid} --group 0 ${mnt_mq}/run/runmqserver
buildah run --user root $ctr_mq -- touch /run/termination-log
buildah run --user root $ctr_mq -- chown mqm:root /run/termination-log
buildah run --user root $ctr_mq -- chmod 0660 /run/termination-log
# Copy in licenses from installed packages
install --mode 0550 --owner root --group root ./mq-advanced-server-rhel/writePackages.sh ${mnt_mq}/usr/local/bin/writePackages
buildah run --user root $ctr_mq -- /usr/local/bin/writePackages
###############################################################################
# Final Buildah commands
@@ -133,7 +157,7 @@ buildah config \
--env LANG=en_US.UTF-8 \
--env LOG_FORMAT=basic \
--entrypoint runmqserver \
--user root \
--user ${mqm_uid} \
$ctr_mq
buildah unmount $ctr_mq
buildah commit $ctr_mq $tag

View File

@@ -1,6 +1,6 @@
#!/bin/bash
# -*- mode: sh -*-
# © Copyright IBM Corporation 2018
# © Copyright IBM Corporation 2018, 2019
#
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,7 +20,7 @@
set -ex
function usage {
echo "Usage: $0 ARCHIVENAME TAG"
echo "Usage: $0 REDIST-ARCHIVE-NAME TAG"
exit 20
}
@@ -29,41 +29,30 @@ if [ "$#" -ne 2 ]; then
usage
fi
readonly mq_archive=downloads/$1
readonly mq_redist_archive=downloads/$1
readonly tag=$2
# Use plain RHEL 7 container
# Note: Red Hat's devtools/go-toolset-7-rhel7 image doesn't allow use of 'root'
# user required for installing the MQ SDK
readonly ctr_mq=$(buildah from rhel7)
# Use Red Hat's Go toolset image as the base
readonly ctr_mq=$(buildah from devtools/go-toolset-7-rhel7)
if [ -z "$ctr_mq" ]
then
echo "ERROR: ctr_mq is empty. Check above output for errors"
exit 50
fi
readonly mnt_mq=$(buildah mount $ctr_mq)
if [ -z "$mnt_mq" ]
readonly mnt_mq_go=$(buildah mount $ctr_mq)
if [ -z "$mnt_mq_go" ]
then
echo "ERROR: mnt_mq is empty. Check above output for errors"
echo "ERROR: mnt_mq_go is empty. Check above output for errors"
exit 50
fi
# Add mqm user
sudo groupadd --root $mnt_mq --system --gid 888 mqm
sudo useradd --root $mnt_mq --system --uid 888 --gid mqm mqm
sudo usermod --root $mnt_mq -aG root mqm
sudo usermod --root $mnt_mq -aG mqm root
# Install the MQ redistributable client (including header files) into the Go builder image
mkdir -p ${mnt_mq_go}/opt/mqm
tar -xzf ${mq_redist_archive} -C ${mnt_mq_go}/opt/mqm
# Enable Yum repository for "optional" RPMs, which is needed for "golang"
buildah run ${ctr_mq} -- yum-config-manager --enable rhel-7-server-optional-rpms
# Install Go compiler
buildah run ${ctr_mq} -- yum install -y golang git gcc
# Install the MQ SDK into the Go builder image
./mq-advanced-server-rhel/install-mq-rhel.sh ${ctr_mq} "${mnt_mq}" "${mq_archive}" "MQSeriesRuntime-*.rpm MQSeriesSDK-*.rpm MQSeriesSamples*.rpm"
# Clean up Yum files
buildah run ${ctr_mq} -- yum clean all --releasever 7
rm -rf ${mnt_mq}/var/cache/yum/*
rm -rf ${mnt_mq_go}/etc/yum.repos.d/*
buildah unmount ${ctr_mq}
# Set environment variables for MQ/Go compilation
buildah config \

View File

@@ -1,6 +1,6 @@
#!/bin/bash
# -*- mode: sh -*-
# © Copyright IBM Corporation 2018
# © Copyright IBM Corporation 2018, 2019
#
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,9 +15,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
# Build a RHEL image, using the buildah tool
# Usage
# mq-buildah.sh ARCHIVEFILE PACKAGES
# Build a RHEL image of MQ Advanced for Developers, using the buildah tool
set -x
set -e
@@ -55,26 +53,37 @@ fi
readonly tag=$2
readonly version=$3
readonly mqm_uid=888
readonly mqm_gid=888
# WARNING: This is what allows the mqm user to change the password of any other user
# It's used by runmqdevserver to change the admin/app passwords.
echo "mqm ALL = NOPASSWD: /usr/sbin/chpasswd" > $mnt_mq/etc/sudoers.d/mq-dev-config
useradd --root $mnt_mq --gid mqm admin
groupadd --root $mnt_mq --system mqclient
useradd --root $mnt_mq --gid mqclient app
# Run these commands inside the container so that the SELinux context is handled correctly
buildah run --user root $ctr_mq -- useradd --gid mqm admin
buildah run --user root $ctr_mq -- groupadd --system mqclient
buildah run --user root $ctr_mq -- useradd --gid mqclient app
buildah run --user root $ctr_mq -- bash -c "echo admin:passw0rd | chpasswd"
buildah run $ctr_mq -- id admin
buildah run $ctr_mq -- sh -c "echo admin:passw0rd | chpasswd"
mkdir -p $mnt_mq/run/runmqdevserver
chown 888:888 $mnt_mq/run/runmqdevserver
mkdir --parents $mnt_mq/run/runmqdevserver
chown ${mqm_uid}:${mqm_gid} $mnt_mq/run/runmqdevserver
# Copy runmqdevserver program
install --mode 0750 --owner 888 --group 888 ./build/runmqdevserver ${mnt_mq}/usr/local/bin/
install --mode 0750 --owner ${mqm_uid} --group ${mqm_gid} ./build/runmqdevserver ${mnt_mq}/usr/local/bin/
install --directory --mode 0775 --owner ${mqm_uid} --group 0 ${mnt_mq}/run/runmqdevserver
# Copy template files
cp incubating/mqadvanced-server-dev/*.tpl ${mnt_mq}/etc/mqm/
cp ./incubating/mqadvanced-server-dev/*.tpl ${mnt_mq}/etc/mqm/
# Copy web XML files for default developer configuration
cp -R incubating/mqadvanced-server-dev/web ${mnt_mq}/etc/mqm/web
mkdir --parents ${mnt_mq}/etc/mqm/web
cp --recursive ./incubating/mqadvanced-server-dev/web/* ${mnt_mq}/etc/mqm/web/
# Make "mqm" the owner of all the config files
chown --recursive ${mqm_uid}:${mqm_gid} ${mnt_mq}/etc/mqm/*
chmod --recursive 0750 ${mnt_mq}/etc/mqm/*
###############################################################################
# Final Buildah commands
@@ -105,7 +114,7 @@ buildah config \
--env MQ_ADMIN_PASSWORD=passw0rd \
--env MQ_DEV=true \
--entrypoint runmqdevserver \
--user root \
--user ${mqm_uid} \
$ctr_mq
buildah unmount $ctr_mq
buildah commit $ctr_mq $tag

View File

@@ -0,0 +1,30 @@
#!/bin/bash
# -*- mode: sh -*-
# © Copyright IBM Corporation 2018, 2019
#
#
# 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.
# Copy in licenses from installed packages
set -e
rm -f /licenses/installed_package_notices
for p in $(rpm -qa | sort)
do
rpm -qi $p >> /licenses/installed_package_notices
printf "\n" >> /licenses/installed_package_notices
done
chmod 0444 /licenses/installed_package_notices

View File

@@ -18,7 +18,7 @@
[[constraint]]
name = "github.com/docker/go-connections"
version = "0.3.0"
version = "0.4.0"
[prune]
go-tests = true

View File

@@ -33,6 +33,7 @@ import (
// Note: This test requires a separate container image to be available for the JMS tests.
func TestDevGoldenPath(t *testing.T) {
t.Parallel()
cli, err := client.NewEnvClient()
if err != nil {
t.Fatal(err)
@@ -66,6 +67,7 @@ func TestDevGoldenPath(t *testing.T) {
// Note: This test requires a separate container image to be available for the JMS tests
func TestDevSecure(t *testing.T) {
t.Parallel()
cli, err := client.NewEnvClient()
if err != nil {
t.Fatal(err)
@@ -127,6 +129,7 @@ func TestDevSecure(t *testing.T) {
func TestDevWebDisabled(t *testing.T) {
t.Parallel()
cli, err := client.NewEnvClient()
if err != nil {
t.Fatal(err)
@@ -157,6 +160,7 @@ func TestDevWebDisabled(t *testing.T) {
func TestDevConfigDisabled(t *testing.T) {
t.Parallel()
cli, err := client.NewEnvClient()
if err != nil {
t.Fatal(err)

View File

@@ -1,7 +1,7 @@
// +build mqdev
/*
© Copyright IBM Corporation 2018
© Copyright IBM Corporation 2018, 2019
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -114,7 +114,7 @@ func runJMSTests(t *testing.T, cli *client.Client, ID string, tls bool, user, pa
t.Fatal(err)
}
startContainer(t, cli, ctr.ID)
rc := waitForContainer(t, cli, ctr.ID, 10)
rc := waitForContainer(t, cli, ctr.ID, 2*time.Minute)
if rc != 0 {
t.Errorf("JUnit container failed with rc=%v", rc)
}

View File

@@ -1,5 +1,5 @@
/*
© Copyright IBM Corporation 2017, 2018
© Copyright IBM Corporation 2017, 2019
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -34,10 +34,13 @@ import (
"github.com/docker/docker/api/types/network"
"github.com/docker/docker/client"
"github.com/docker/go-connections/nat"
"github.com/ibm-messaging/mq-container/internal/command"
)
func TestLicenseNotSet(t *testing.T) {
t.Parallel()
cli, err := client.NewEnvClient()
if err != nil {
t.Fatal(err)
@@ -45,15 +48,16 @@ func TestLicenseNotSet(t *testing.T) {
containerConfig := container.Config{}
id := runContainer(t, cli, &containerConfig)
defer cleanContainer(t, cli, id)
rc := waitForContainer(t, cli, id, 5)
rc := waitForContainer(t, cli, id, 20*time.Second)
if rc != 1 {
t.Errorf("Expected rc=1, got rc=%v", rc)
}
expectTerminationMessage(t)
expectTerminationMessage(t, cli, id)
}
func TestLicenseView(t *testing.T) {
t.Parallel()
cli, err := client.NewEnvClient()
if err != nil {
t.Fatal(err)
@@ -63,7 +67,7 @@ func TestLicenseView(t *testing.T) {
}
id := runContainer(t, cli, &containerConfig)
defer cleanContainer(t, cli, id)
rc := waitForContainer(t, cli, id, 5)
rc := waitForContainer(t, cli, id, 20*time.Second)
if rc != 1 {
t.Errorf("Expected rc=1, got rc=%v", rc)
}
@@ -77,12 +81,14 @@ func TestLicenseView(t *testing.T) {
// TestGoldenPath starts a queue manager successfully when metrics are enabled
func TestGoldenPathWithMetrics(t *testing.T) {
t.Parallel()
goldenPath(t, true)
}
// TestGoldenPath starts a queue manager successfully when metrics are disabled
func TestGoldenPathNoMetrics(t *testing.T) {
t.Parallel()
goldenPath(t, false)
}
@@ -106,10 +112,11 @@ func goldenPath(t *testing.T, metric bool) {
stopContainer(t, cli, id)
}
// TestSecurityVulnerabilities checks for any vulnerabilities in the image, as reported
// TestSecurityVulnerabilitiesUbuntu checks for any vulnerabilities in the image, as reported
// by Ubuntu
func TestSecurityVulnerabilities(t *testing.T) {
func TestSecurityVulnerabilitiesUbuntu(t *testing.T) {
t.Parallel()
cli, err := client.NewEnvClient()
if err != nil {
t.Fatal(err)
@@ -135,6 +142,47 @@ func TestSecurityVulnerabilities(t *testing.T) {
}
}
// TestSecurityVulnerabilitiesRedHat checks for any vulnerabilities in the image, as reported
// by Red Hat
func TestSecurityVulnerabilitiesRedHat(t *testing.T) {
t.Parallel()
cli, err := client.NewEnvClient()
if err != nil {
t.Fatal(err)
}
_, ret, _ := command.Run("bash", "-c", "test -f /etc/redhat-release")
if ret != 0 {
t.Skip("Skipping test because host is not RedHat-based")
}
rc, _ := runContainerOneShot(t, cli, "bash", "-c", "test -f /etc/redhat-release")
if rc != 0 {
t.Skip("Skipping test because container is not RedHat-based")
}
id, _, err := command.Run("sudo", "buildah", "from", imageName())
if err != nil {
t.Log(id)
t.Fatal(err)
}
id = strings.TrimSpace(id)
defer command.Run("buildah", "rm", id)
mnt, _, err := command.Run("sudo", "buildah", "mount", id)
if err != nil {
t.Log(mnt)
t.Fatal(err)
}
mnt = strings.TrimSpace(mnt)
out, _, err := command.Run("bash", "-c", "sudo cp /etc/yum.repos.d/* "+filepath.Join(mnt, "/etc/yum.repos.d/"))
if err != nil {
t.Log(out)
t.Fatal(err)
}
out, ret, _ = command.Run("bash", "-c", "yum --installroot="+mnt+" updateinfo list sec | grep /Sec")
if ret != 1 {
t.Errorf("Expected no vulnerabilities, found the following:\n%v", out)
}
}
func utilTestNoQueueManagerName(t *testing.T, hostName string, expectedName string) {
search := "QMNAME(" + expectedName + ")"
cli, err := client.NewEnvClient()
@@ -155,11 +203,13 @@ func utilTestNoQueueManagerName(t *testing.T, hostName string, expectedName stri
}
func TestNoQueueManagerName(t *testing.T) {
t.Parallel()
utilTestNoQueueManagerName(t, "test", "test")
}
func TestNoQueueManagerNameInvalidHostname(t *testing.T) {
t.Parallel()
utilTestNoQueueManagerName(t, "test-1", "test1")
}
@@ -167,6 +217,7 @@ func TestNoQueueManagerNameInvalidHostname(t *testing.T) {
// container and starts a new one with same volume. With metrics enabled
func TestWithVolumeAndMetrics(t *testing.T) {
t.Parallel()
withVolume(t, true)
}
@@ -174,6 +225,7 @@ func TestWithVolumeAndMetrics(t *testing.T) {
// container and starts a new one with same volume. With metrics disabled
func TestWithVolumeNoMetrics(t *testing.T) {
t.Parallel()
withVolume(t, false)
}
@@ -225,6 +277,7 @@ func withVolume(t *testing.T, metric bool) {
// and restarted cleanly
func TestNoVolumeWithRestart(t *testing.T) {
t.Parallel()
cli, err := client.NewEnvClient()
if err != nil {
t.Fatal(err)
@@ -240,58 +293,139 @@ func TestNoVolumeWithRestart(t *testing.T) {
waitForReady(t, cli, id)
}
// TestCreateQueueManagerFail causes a failure of `crtmqm`
func TestCreateQueueManagerFail(t *testing.T) {
t.Parallel()
// TestVolumeRequiresRoot tests the case where only the root user can write
// to the persistent volume. In this case, an "init container" is needed,
// where `runmqserver -i` is run to initialize the storage. Then the
// container can be run as normal.
func TestVolumeRequiresRoot(t *testing.T) {
cli, err := client.NewEnvClient()
if err != nil {
t.Fatal(err)
}
img, _, err := cli.ImageInspectWithRaw(context.Background(), imageName())
vol := createVolume(t, cli)
defer removeVolume(t, cli, vol.Name)
// Set permissions on the volume to only allow root to write it
// It's important that read and execute permissions are given to other users
rc, _ := runContainerOneShotWithVolume(t, cli, vol.Name+":/mnt/mqm:nocopy", "bash", "-c", "chown 65534:4294967294 /mnt/mqm/ && chmod 0755 /mnt/mqm/ && ls -lan /mnt/mqm/")
if rc != 0 {
t.Errorf("Expected one shot container to return rc=0, got rc=%v", rc)
}
containerConfig := container.Config{
Image: imageName(),
Env: []string{"LICENSE=accept", "MQ_QMGR_NAME=qm1"},
}
hostConfig := container.HostConfig{
Binds: []string{
coverageBind(t),
vol.Name + ":/mnt/mqm:nocopy",
},
}
networkingConfig := network.NetworkingConfig{}
// Run an "init container" as root, with the "-i" option, to initialize the volume
containerConfig = container.Config{
Image: imageName(),
Env: []string{"LICENSE=accept", "MQ_QMGR_NAME=qm1", "DEBUG=true"},
User: "0",
Entrypoint: []string{"runmqserver", "-i"},
}
initCtr, err := cli.ContainerCreate(context.Background(), &containerConfig, &hostConfig, &networkingConfig, t.Name()+"Init")
if err != nil {
t.Fatal(err)
}
oldEntrypoint := strings.Join(img.Config.Entrypoint, " ")
defer cleanContainer(t, cli, initCtr.ID)
t.Logf("Init container ID=%v", initCtr.ID)
startContainer(t, cli, initCtr.ID)
rc = waitForContainer(t, cli, initCtr.ID, 20*time.Second)
if rc != 0 {
t.Errorf("Expected init container to exit with rc=0, got rc=%v", rc)
}
containerConfig = container.Config{
Image: imageName(),
Env: []string{"LICENSE=accept", "MQ_QMGR_NAME=qm1", "DEBUG=true"},
}
ctr, err := cli.ContainerCreate(context.Background(), &containerConfig, &hostConfig, &networkingConfig, t.Name()+"Main")
if err != nil {
t.Fatal(err)
}
defer cleanContainer(t, cli, ctr.ID)
t.Logf("Main container ID=%v", ctr.ID)
startContainer(t, cli, ctr.ID)
waitForReady(t, cli, ctr.ID)
}
// TestCreateQueueManagerFail causes a failure of `crtmqm`
func TestCreateQueueManagerFail(t *testing.T) {
t.Parallel()
cli, err := client.NewEnvClient()
if err != nil {
t.Fatal(err)
}
var files = []struct {
Name, Body string
}{
{"Dockerfile", fmt.Sprintf(`
FROM %v
USER root
RUN echo '#!/bin/bash\nexit 999' > /opt/mqm/bin/crtmqm
RUN chown mqm:mqm /opt/mqm/bin/crtmqm
RUN chmod 6550 /opt/mqm/bin/crtmqm
USER mqm`, imageName())},
}
tag := createImage(t, cli, files)
defer deleteImage(t, cli, tag)
containerConfig := container.Config{
Env: []string{"LICENSE=accept", "MQ_QMGR_NAME=qm1"},
// 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},
Image: tag,
}
id := runContainer(t, cli, &containerConfig)
defer cleanContainer(t, cli, id)
rc := waitForContainer(t, cli, id, 10)
rc := waitForContainer(t, cli, id, 10*time.Second)
if rc != 1 {
t.Errorf("Expected rc=1, got rc=%v", rc)
}
expectTerminationMessage(t)
expectTerminationMessage(t, cli, id)
}
// TestStartQueueManagerFail causes a failure of `strmqm`
func TestStartQueueManagerFail(t *testing.T) {
t.Parallel()
cli, err := client.NewEnvClient()
if err != nil {
t.Fatal(err)
}
img, _, err := cli.ImageInspectWithRaw(context.Background(), imageName())
if err != nil {
t.Fatal(err)
var files = []struct {
Name, Body string
}{
{"Dockerfile", fmt.Sprintf(`
FROM %v
USER root
RUN echo '#!/bin/bash\ndltmqm $@ && strmqm $@' > /opt/mqm/bin/strmqm
RUN chown mqm:mqm /opt/mqm/bin/strmqm
RUN chmod 6550 /opt/mqm/bin/strmqm
USER mqm`, imageName())},
}
oldEntrypoint := strings.Join(img.Config.Entrypoint, " ")
tag := createImage(t, cli, files)
defer deleteImage(t, cli, tag)
containerConfig := container.Config{
Env: []string{"LICENSE=accept", "MQ_QMGR_NAME=qm1", "DEBUG=1"},
// Override the entrypoint to replace `strmqm` with a script which deletes the queue manager.
// This will cause `strmqm` to return with an exit code of 72.
Entrypoint: []string{"bash", "-c", "echo '#!/bin/bash\ndltmqm $@ && strmqm $@' > /opt/mqm/bin/strmqm && exec " + oldEntrypoint},
Env: []string{"LICENSE=accept", "MQ_QMGR_NAME=qm1"},
Image: tag,
}
id := runContainer(t, cli, &containerConfig)
defer cleanContainer(t, cli, id)
rc := waitForContainer(t, cli, id, 10)
rc := waitForContainer(t, cli, id, 20*time.Second)
if rc != 1 {
t.Errorf("Expected rc=1, got rc=%v", rc)
}
expectTerminationMessage(t)
expectTerminationMessage(t, cli, id)
}
// TestVolumeUnmount runs a queue manager with a volume, and then forces an
@@ -300,6 +434,7 @@ func TestStartQueueManagerFail(t *testing.T) {
// attached storage gets unmounted.
func TestVolumeUnmount(t *testing.T) {
t.Parallel()
cli, err := client.NewEnvClient()
if err != nil {
t.Fatal(err)
@@ -348,6 +483,7 @@ func TestVolumeUnmount(t *testing.T) {
// 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)
@@ -384,6 +520,7 @@ func TestZombies(t *testing.T) {
// on that image, and checks that the MQSC has been applied correctly.
func TestMQSC(t *testing.T) {
t.Parallel()
cli, err := client.NewEnvClient()
if err != nil {
t.Fatal(err)
@@ -391,7 +528,13 @@ func TestMQSC(t *testing.T) {
var files = []struct {
Name, Body string
}{
{"Dockerfile", fmt.Sprintf("FROM %v\nRUN rm -f /etc/mqm/*.mqsc\nADD test.mqsc /etc/mqm/", imageName())},
{"Dockerfile", fmt.Sprintf(`
FROM %v
USER root
RUN rm -f /etc/mqm/*.mqsc
ADD test.mqsc /etc/mqm/
RUN chmod 0660 /etc/mqm/test.mqsc
USER mqm`, imageName())},
{"test.mqsc", "DEFINE QLOCAL(test)"},
}
tag := createImage(t, cli, files)
@@ -411,11 +554,144 @@ func TestMQSC(t *testing.T) {
}
}
// TestLargeMQSC creates a new image with a large MQSC file in, starts a container based
// on that image, and checks that the MQSC has been applied correctly.
func TestLargeMQSC(t *testing.T) {
t.Parallel()
cli, err := client.NewEnvClient()
if err != nil {
t.Fatal(err)
}
const numQueues = 1000
var buf bytes.Buffer
for i := 1; i <= numQueues; i++ {
fmt.Fprintf(&buf, "* Test processing of a large MQSC file, defining queue test%v\nDEFINE QLOCAL(test%v)\n", i, i)
}
var files = []struct {
Name, Body string
}{
{"Dockerfile", fmt.Sprintf(`
FROM %v
USER root
RUN rm -f /etc/mqm/*.mqsc
ADD test.mqsc /etc/mqm/
RUN chmod 0660 /etc/mqm/test.mqsc
USER mqm`, imageName())},
{"test.mqsc", buf.String()},
}
tag := createImage(t, cli, files)
defer deleteImage(t, cli, tag)
containerConfig := container.Config{
Env: []string{"LICENSE=accept", "MQ_QMGR_NAME=qm1"},
Image: tag,
}
id := runContainer(t, cli, &containerConfig)
defer cleanContainer(t, cli, id)
waitForReady(t, cli, id)
rc, mqscOutput := execContainer(t, cli, id, "mqm", []string{"bash", "-c", "echo 'DISPLAY QLOCAL(test" + strconv.Itoa(numQueues) + ")' | runmqsc"})
if rc != 0 {
r := regexp.MustCompile("AMQ[0-9][0-9][0-9][0-9]E")
t.Fatalf("Expected runmqsc to exit with rc=0, got %v with error %v", rc, r.FindString(mqscOutput))
}
}
// TestRedactMQSC creates a new image with a MQSC file that contains sensitive information, starts a container based
// on that image, and checks that the MQSC has been redacted in the logs.
func TestRedactMQSC(t *testing.T) {
t.Parallel()
cli, err := client.NewEnvClient()
if err != nil {
t.Fatal(err)
}
var buf bytes.Buffer
sslcryp := "GSK_PKCS11=/usr/lib/pkcs11/PKCS11_API.so;token-label;token-password;SYMMETRIC_CIPHER_ON;"
fmt.Fprintf(&buf, "*TEST-REDACT-MQSC: A(1) LDAPPWD(abcdefgh) B(2) PASSWORD(abcdefgh) C(3) SSLCRYP(%v) D(4)\n", sslcryp)
fmt.Fprintf(&buf, "*TEST-REDACT-MQSC: A(1) ldappwd(12345678) B(2) password(12345678) C(3) sslcryp(%v) D(4)\n", sslcryp)
fmt.Fprintf(&buf, "*TEST-REDACT-MQSC: A(1) LdapPwd('12?@!$Gh') B(2) Password('12?@!$Gh') C(3) SSLCryp(%v) D(4)\n", sslcryp)
fmt.Fprintf(&buf, "*TEST-REDACT-MQSC: A(1) LDAPPWD (abcdefgh) B(2) PASSWORD\t(abcdefgh) C(3) SSLCRYP \t (%v) D(4)", sslcryp)
var files = []struct {
Name, Body string
}{
{"Dockerfile", fmt.Sprintf(`
FROM %v
USER root
RUN rm -f /etc/mqm/*.mqsc
ADD test.mqsc /etc/mqm/
RUN chmod 0660 /etc/mqm/test.mqsc
USER mqm`, imageName())},
{"test.mqsc", buf.String()},
}
tag := createImage(t, cli, files)
defer deleteImage(t, cli, tag)
containerConfig := container.Config{
Env: []string{"LICENSE=accept", "MQ_QMGR_NAME=qm1"},
Image: tag,
}
id := runContainer(t, cli, &containerConfig)
defer cleanContainer(t, cli, id)
waitForReady(t, cli, id)
stopContainer(t, cli, id)
scanner := bufio.NewScanner(strings.NewReader(inspectLogs(t, cli, id)))
expectedOutput := "*TEST-REDACT-MQSC: A(1) LDAPPWD(*********) B(2) PASSWORD(*********) C(3) SSLCRYP(*********) D(4)"
for scanner.Scan() {
s := scanner.Text()
if strings.Contains(s, "*TEST-REDACT-MQSC:") && !strings.Contains(s, expectedOutput) {
t.Fatalf("Expected redacted MQSC output, got: %v", s)
}
}
err = scanner.Err()
if err != nil {
t.Fatal(err)
}
}
// TestInvalidMQSC creates a new image with an MQSC file containing invalid MQSC,
// tries to start a container based on that image, and checks that container terminates
// func TestInvalidMQSC(t *testing.T) {
// t.Parallel()
// cli, err := client.NewEnvClient()
// if err != nil {
// t.Fatal(err)
// }
// var files = []struct {
// Name, Body string
// }{
// {"Dockerfile", fmt.Sprintf(`
// FROM %v
// USER root
// RUN rm -f /etc/mqm/*.mqsc
// ADD mqscTest.mqsc /etc/mqm/
// RUN chmod 0660 /etc/mqm/mqscTest.mqsc
// USER mqm`, imageName())},
// {"mqscTest.mqsc", "DEFINE INVALIDLISTENER('TEST.LISTENER.TCP') TRPTYPE(TCP) PORT(1414) CONTROL(QMGR) REPLACE"},
// }
// tag := createImage(t, cli, files)
// defer deleteImage(t, cli, tag)
// containerConfig := container.Config{
// Env: []string{"LICENSE=accept", "MQ_QMGR_NAME=qm1"},
// Image: tag,
// }
// id := runContainer(t, cli, &containerConfig)
// defer cleanContainer(t, cli, id)
// rc := waitForContainer(t, cli, id, 60*time.Second)
// if rc != 1 {
// t.Errorf("Expected rc=1, got rc=%v", rc)
// }
// expectTerminationMessage(t, cli, id)
// }
// TestReadiness creates a new image with large amounts of MQSC in, to
// ensure that the readiness check doesn't pass until configuration has finished.
// WARNING: This test is sensitive to the speed of the machine it's running on.
func TestReadiness(t *testing.T) {
t.Parallel()
cli, err := client.NewEnvClient()
if err != nil {
t.Fatal(err)
@@ -428,7 +704,13 @@ func TestReadiness(t *testing.T) {
var files = []struct {
Name, Body string
}{
{"Dockerfile", fmt.Sprintf("FROM %v\nRUN rm -f /etc/mqm/*.mqsc\nADD test.mqsc /etc/mqm/", imageName())},
{"Dockerfile", fmt.Sprintf(`
FROM %v
USER root
RUN rm -f /etc/mqm/*.mqsc
ADD test.mqsc /etc/mqm/
RUN chmod 0660 /etc/mqm/test.mqsc
USER mqm`, imageName())},
{"test.mqsc", buf.String()},
}
tag := createImage(t, cli, files)
@@ -464,22 +746,34 @@ func TestReadiness(t *testing.T) {
func TestErrorLogRotation(t *testing.T) {
t.Parallel()
cli, err := client.NewEnvClient()
if err != nil {
t.Fatal(err)
}
logsize := 65536
rc, _ := runContainerOneShot(t, cli, "bash", "-c", "test -d /etc/apt")
if rc != 0 {
// RHEL
logsize = 32768
}
qmName := "qm1"
containerConfig := container.Config{
Env: []string{
"LICENSE=accept",
"MQ_QMGR_NAME=" + qmName,
"MQMAXERRORLOGSIZE=65536",
fmt.Sprintf("MQMAXERRORLOGSIZE=%d", logsize),
"LOG_FORMAT=json",
fmt.Sprintf("AMQ_EXTRA_QM_STANZAS=QMErrorLog:ErrorLogSize=%d", logsize),
},
ExposedPorts: nat.PortSet{
"1414/tcp": struct{}{},
},
}
id := runContainer(t, cli, &containerConfig)
defer cleanContainer(t, cli, id)
waitForReady(t, cli, id)
@@ -487,6 +781,7 @@ func TestErrorLogRotation(t *testing.T) {
// Generate some content for the error logs, by trying to put messages under an unauthorized user
// execContainer(t, cli, id, "fred", []string{"bash", "-c", "for i in {1..30} ; do /opt/mqm/samp/bin/amqsput FAKE; done"})
execContainer(t, cli, id, "root", []string{"useradd", "fred"})
for {
execContainer(t, cli, id, "fred", []string{"bash", "-c", "/opt/mqm/samp/bin/amqsput FAKE"})
@@ -529,12 +824,14 @@ func TestErrorLogRotation(t *testing.T) {
// Tests the log comes out in JSON format when JSON format is enabled. With metrics enabled
func TestJSONLogFormatWithMetrics(t *testing.T) {
t.Parallel()
jsonLogFormat(t, true)
}
// Tests the log comes out in JSON format when JSON format is enabled. With metrics disabled
func TestJSONLogFormatNoMetrics(t *testing.T) {
t.Parallel()
jsonLogFormat(t, false)
}
@@ -575,6 +872,7 @@ func jsonLogFormat(t *testing.T, metric bool) {
func TestBadLogFormat(t *testing.T) {
t.Parallel()
cli, err := client.NewEnvClient()
if err != nil {
t.Fatal(err)
@@ -587,11 +885,11 @@ func TestBadLogFormat(t *testing.T) {
}
id := runContainer(t, cli, &containerConfig)
defer cleanContainer(t, cli, id)
rc := waitForContainer(t, cli, id, 5)
rc := waitForContainer(t, cli, id, 20*time.Second)
if rc != 1 {
t.Errorf("Expected rc=1, got rc=%v", rc)
}
expectTerminationMessage(t)
expectTerminationMessage(t, cli, id)
}
// TestMQJSONDisabled tests the case where MQ's JSON logging feature is

View File

@@ -1,5 +1,5 @@
/*
© Copyright IBM Corporation 2017, 2018
© Copyright IBM Corporation 2017, 2019
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -43,6 +43,18 @@ import (
"github.com/docker/go-connections/nat"
)
type containerDetails struct {
ID string
Name string
Image string
Path string
Args []string
CapAdd []string
CapDrop []string
User string
Env []string
}
func imageName() string {
image, ok := os.LookupEnv("TEST_IMAGE")
if !ok {
@@ -59,6 +71,29 @@ func imageNameDevJMS() string {
return image
}
// baseImage returns the ID of the underlying base image (e.g. "ubuntu" or "rhel")
func baseImage(t *testing.T, cli *client.Client) string {
rc, out := runContainerOneShot(t, cli, "grep", "^ID=", "/etc/os-release")
if rc != 0 {
t.Fatal("Couldn't determine base image")
}
s := strings.Split(out, "=")
if len(s) < 2 {
t.Fatal("Couldn't determine base image string")
}
return s[1]
}
// devImage returns true if the image under test is a developer image,
// determined by use of the MQ_ADMIN_PASSWORD environment variable
func devImage(t *testing.T, cli *client.Client) bool {
rc, _ := runContainerOneShot(t, cli, "printenv", "MQ_ADMIN_PASSWORD")
if rc == 0 {
return true
}
return false
}
// isWSL return whether we are running in the Windows Subsystem for Linux
func isWSL(t *testing.T) bool {
if runtime.GOOS == "linux" {
@@ -124,66 +159,79 @@ func getTempDir(t *testing.T, unixStylePath bool) string {
return "/tmp/"
}
// terminationLogUnixPath returns the name of the file to use for the termination log message, with a UNIX path
func terminationLogUnixPath(t *testing.T) string {
// Warning: this directory must be accessible to the Docker daemon,
// in order to enable the bind mount
return getTempDir(t, true) + t.Name() + "-termination-log"
}
// terminationLogOSPath returns the name of the file to use for the termination log message, with an OS specific path
func terminationLogOSPath(t *testing.T) string {
// Warning: this directory must be accessible to the Docker daemon,
// in order to enable the bind mount
return getTempDir(t, false) + t.Name() + "-termination-log"
}
// terminationBind returns a string to use to bind-mount a termination log file.
// This is done using a bind, because you can't copy files from /dev out of the container.
func terminationBind(t *testing.T) string {
n := terminationLogUnixPath(t)
// Remove it if it already exists
os.Remove(n)
// Create the empty file
f, err := os.OpenFile(n, os.O_WRONLY|os.O_CREATE, 0600)
if err != nil {
t.Fatal(err)
}
f.Close()
return terminationLogOSPath(t) + ":/dev/termination-log"
}
// terminationMessage return the termination message, or an empty string if not set
func terminationMessage(t *testing.T) string {
b, err := ioutil.ReadFile(terminationLogUnixPath(t))
func terminationMessage(t *testing.T, cli *client.Client, ID string) string {
r, _, err := cli.CopyFromContainer(context.Background(), ID, "/run/termination-log")
if err != nil {
t.Log(err)
return ""
}
return string(b)
b, err := ioutil.ReadAll(r)
tr := tar.NewReader(bytes.NewReader(b))
_, err = tr.Next()
if err != nil {
t.Log(err)
return ""
}
// read the complete content of the file h.Name into the bs []byte
content, err := ioutil.ReadAll(tr)
if err != nil {
t.Log(err)
return ""
}
return string(content)
}
func expectTerminationMessage(t *testing.T) {
m := terminationMessage(t)
func expectTerminationMessage(t *testing.T, cli *client.Client, ID string) {
m := terminationMessage(t, cli, ID)
if m == "" {
t.Error("Expected termination message to be set")
}
}
func cleanContainer(t *testing.T, cli *client.Client, ID string) {
// logContainerDetails logs selected details about the container
func logContainerDetails(t *testing.T, cli *client.Client, ID string) {
i, err := cli.ContainerInspect(context.Background(), ID)
if err == nil {
// Log the results and continue
t.Logf("Inspected container %v: %#v", ID, i)
s, err := json.MarshalIndent(i, "", " ")
d := containerDetails{
ID: ID,
Name: i.Name,
Image: i.Image,
Path: i.Path,
Args: i.Args,
CapAdd: i.HostConfig.CapAdd,
CapDrop: i.HostConfig.CapDrop,
User: i.Config.User,
Env: i.Config.Env,
}
// If you need more details, you can always just run `json.MarshalIndent(i, "", " ")` to see everything.
t.Logf("Container details: %+v", d)
}
}
func cleanContainerQuiet(t *testing.T, cli *client.Client, ID string) {
timeout := 10 * time.Second
err := cli.ContainerStop(context.Background(), ID, &timeout)
if err != nil {
t.Fatal(err)
// Just log the error and continue
t.Log(err)
}
t.Logf("Inspected container %v: %v", ID, string(s))
opts := types.ContainerRemoveOptions{
RemoveVolumes: true,
Force: true,
}
err = cli.ContainerRemove(context.Background(), ID, opts)
if err != nil {
t.Error(err)
}
}
func cleanContainer(t *testing.T, cli *client.Client, ID string) {
logContainerDetails(t, cli, ID)
t.Logf("Stopping container: %v", ID)
timeout := 10 * time.Second
// Stop the container. This allows the coverage output to be generated.
err = cli.ContainerStop(context.Background(), ID, &timeout)
err := cli.ContainerStop(context.Background(), ID, &timeout)
if err != nil {
// Just log the error and continue
t.Log(err)
@@ -195,11 +243,10 @@ func cleanContainer(t *testing.T, cli *client.Client, ID string) {
// Log the container output for any container we're about to delete
t.Logf("Console log from container %v:\n%v", ID, inspectTextLogs(t, cli, ID))
m := terminationMessage(t)
m := terminationMessage(t, cli, ID)
if m != "" {
t.Logf("Termination message: %v", m)
}
os.Remove(terminationLogUnixPath(t))
t.Logf("Removing container: %s", ID)
opts := types.ContainerRemoveOptions{
@@ -219,15 +266,36 @@ func runContainerWithPorts(t *testing.T, cli *client.Client, containerConfig *co
if containerConfig.Image == "" {
containerConfig.Image = imageName()
}
// Always run as the "mqm" user, unless the test has specified otherwise
if containerConfig.User == "" {
containerConfig.User = "mqm"
}
// if coverage
containerConfig.Env = append(containerConfig.Env, "COVERAGE_FILE="+t.Name()+".cov")
containerConfig.Env = append(containerConfig.Env, "EXIT_CODE_FILE="+getExitCodeFilename(t))
hostConfig := container.HostConfig{
Binds: []string{
coverageBind(t),
terminationBind(t),
},
PortBindings: nat.PortMap{},
CapDrop: []string{
"ALL",
},
}
if devImage(t, cli) {
t.Logf("Detected MQ Advanced for Developers image — adding extra Linux capabilities to container")
hostConfig.CapAdd = []string{
"CHOWN",
"SETUID",
"SETGID",
"AUDIT_WRITE",
}
// Only needed for a RHEL-based image
if baseImage(t, cli) != "ubuntu" {
hostConfig.CapAdd = append(hostConfig.CapAdd, "DAC_OVERRIDE")
}
} else {
t.Logf("Detected MQ Advanced image - dropping all capabilities")
}
for _, p := range ports {
port := nat.Port(fmt.Sprintf("%v/tcp", p))
@@ -254,13 +322,62 @@ func runContainer(t *testing.T, cli *client.Client, containerConfig *container.C
return runContainerWithPorts(t, cli, containerConfig, nil)
}
// runContainerOneShot runs a container with a custom entrypoint, as the root
// user and with default capabilities
func runContainerOneShot(t *testing.T, cli *client.Client, command ...string) (int64, string) {
containerConfig := container.Config{
Entrypoint: command,
User: "root",
Image: imageName(),
}
id := runContainer(t, cli, &containerConfig)
defer cleanContainer(t, cli, id)
return waitForContainer(t, cli, id, 10), inspectLogs(t, cli, id)
hostConfig := container.HostConfig{}
networkingConfig := network.NetworkingConfig{}
t.Logf("Running one shot container (%s): %v", containerConfig.Image, command)
ctr, err := cli.ContainerCreate(context.Background(), &containerConfig, &hostConfig, &networkingConfig, t.Name()+"OneShot")
if err != nil {
t.Fatal(err)
}
startOptions := types.ContainerStartOptions{}
err = cli.ContainerStart(context.Background(), ctr.ID, startOptions)
if err != nil {
t.Fatal(err)
}
defer cleanContainerQuiet(t, cli, ctr.ID)
rc := waitForContainer(t, cli, ctr.ID, 20*time.Second)
out := inspectLogs(t, cli, ctr.ID)
t.Logf("One shot container finished with rc=%v, output=%v", rc, out)
return rc, out
}
// runContainerOneShot runs a container with a custom entrypoint, as the root
// user, with default capabilities, and a volume mounted
func runContainerOneShotWithVolume(t *testing.T, cli *client.Client, bind string, command ...string) (int64, string) {
containerConfig := container.Config{
Entrypoint: command,
User: "root",
Image: imageName(),
}
hostConfig := container.HostConfig{
Binds: []string{
bind,
},
}
networkingConfig := network.NetworkingConfig{}
t.Logf("Running one shot container with volume (%s): %v", containerConfig.Image, command)
ctr, err := cli.ContainerCreate(context.Background(), &containerConfig, &hostConfig, &networkingConfig, t.Name()+"OneShotVolume")
if err != nil {
t.Fatal(err)
}
startOptions := types.ContainerStartOptions{}
err = cli.ContainerStart(context.Background(), ctr.ID, startOptions)
if err != nil {
t.Fatal(err)
}
defer cleanContainerQuiet(t, cli, ctr.ID)
rc := waitForContainer(t, cli, ctr.ID, 20*time.Second)
out := inspectLogs(t, cli, ctr.ID)
t.Logf("One shot container finished with rc=%v, output=%v", rc, out)
return rc, out
}
func startContainer(t *testing.T, cli *client.Client, ID string) {
@@ -309,19 +426,19 @@ func getCoverageExitCode(t *testing.T, orig int64) int64 {
}
// waitForContainer waits until a container has exited
func waitForContainer(t *testing.T, cli *client.Client, ID string, timeout int64) int64 {
rc, err := cli.ContainerWait(context.Background(), ID)
func waitForContainer(t *testing.T, cli *client.Client, ID string, timeout time.Duration) int64 {
c, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
rc, err := cli.ContainerWait(c, ID)
if err != nil {
t.Fatal(err)
}
if coverage() {
// 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)
}
if err != nil {
t.Fatal(err)
}
return rc
}
@@ -395,7 +512,7 @@ func execContainer(t *testing.T, cli *client.Client, ID string, user string, cmd
}
func waitForReady(t *testing.T, cli *client.Client, ID string) {
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
defer cancel()
for {

View File

@@ -1,5 +1,5 @@
/*
© Copyright IBM Corporation 2018
© Copyright IBM Corporation 2018, 2019
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -27,6 +27,7 @@ import (
func TestGoldenPathMetric(t *testing.T) {
t.Parallel()
cli, err := client.NewEnvClient()
if err != nil {
t.Fatal(err)
@@ -53,6 +54,7 @@ func TestGoldenPathMetric(t *testing.T) {
func TestMetricNames(t *testing.T) {
t.Parallel()
cli, err := client.NewEnvClient()
if err != nil {
t.Fatal(err)
@@ -96,6 +98,7 @@ func TestMetricNames(t *testing.T) {
func TestMetricLabels(t *testing.T) {
t.Parallel()
requiredLabels := []string{"qmgr"}
cli, err := client.NewEnvClient()
if err != nil {
@@ -144,6 +147,7 @@ func TestMetricLabels(t *testing.T) {
func TestRapidFirePrometheus(t *testing.T) {
t.Parallel()
cli, err := client.NewEnvClient()
if err != nil {
t.Fatal(err)
@@ -177,6 +181,7 @@ func TestRapidFirePrometheus(t *testing.T) {
func TestSlowPrometheus(t *testing.T) {
t.Parallel()
cli, err := client.NewEnvClient()
if err != nil {
t.Fatal(err)
@@ -207,6 +212,7 @@ func TestSlowPrometheus(t *testing.T) {
func TestContainerRestart(t *testing.T) {
t.Parallel()
cli, err := client.NewEnvClient()
if err != nil {
t.Fatal(err)
@@ -254,6 +260,7 @@ func TestContainerRestart(t *testing.T) {
func TestQMRestart(t *testing.T) {
t.Parallel()
cli, err := client.NewEnvClient()
if err != nil {
t.Fatal(err)
@@ -279,7 +286,7 @@ func TestQMRestart(t *testing.T) {
// Restart just the QM (to simulate a lost connection)
t.Log("Stopping queue manager\n")
rc, out := execContainer(t, cli, id, "mqm", []string{"endmqm", "-w", defaultMetricQMName})
rc, out := execContainer(t, cli, id, "mqm", []string{"endmqm", "-w", "-r", defaultMetricQMName})
if rc != 0 {
t.Fatalf("Failed to stop the queue manager. rc=%d, err=%s", rc, out)
}
@@ -311,6 +318,7 @@ func TestQMRestart(t *testing.T) {
func TestValidValues(t *testing.T) {
t.Parallel()
cli, err := client.NewEnvClient()
if err != nil {
t.Fatal(err)
@@ -346,6 +354,7 @@ func TestValidValues(t *testing.T) {
func TestChangingValues(t *testing.T) {
t.Parallel()
cli, err := client.NewEnvClient()
if err != nil {
t.Fatal(err)

View File

@@ -1,5 +1,5 @@
#!/bin/bash
# © Copyright IBM Corporation 2018
# © Copyright IBM Corporation 2018, 2019
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -22,24 +22,34 @@ set -e
# Use a "scratch" container, so the resulting image has minimal files
# Resulting image won't have yum, for example
readonly ctr_mq=$(buildah from rhel7)
readonly ctr_mq=$(buildah from rhel7-minimal)
readonly mnt_mq=$(buildah mount $ctr_mq)
readonly imagename=$1
buildah run $ctr_mq -- yum install -y \
java-1.7.0-openjdk-devel \
microdnf_opts="--nodocs"
# Check whether the host is registered with Red Hat
if subscription-manager status ; then
# Host is subscribed, but the minimal image has no enabled repos
# Note that the "bc" package is the only one in "extras"
microdnf_opts="${microdnf_opts} --enablerepo=rhel-7-server-rpms --enablerepo=rhel-7-server-extras-rpms"
else
# Use the Yum repositories configured on the host
cp -R /etc/yum.repos.d/* ${mnt_mq}/etc/yum.repos.d/
fi
buildah run ${ctr_mq} -- microdnf ${microdnf_opts} install \
java-1.8.0-openjdk-devel \
java \
which \
wget
buildah run $ctr_mq -- sh -c "cd /tmp && wget http://mirror.olnevhost.net/pub/apache/maven/binaries/apache-maven-3.2.2-bin.tar.gz"
tar xvf $mnt_mq/tmp/apache-maven-3.2.2-bin.tar.gz -C $mnt_mq/tmp/
buildah run $ctr_mq -- sh -c "cd /tmp && wget https://www-eu.apache.org/dist/maven/maven-3/3.6.0/binaries/apache-maven-3.6.0-bin.tar.gz"
tar xvf $mnt_mq/tmp/apache-maven-3.6.0-bin.tar.gz -C $mnt_mq/tmp/
mkdir -p $mnt_mq/usr/src/mymaven
cp pom.xml $mnt_mq/usr/src/mymaven/
cp -R src $mnt_mq/usr/src/mymaven/src
buildah run $ctr_mq -- sh -c "cd /usr/src/mymaven && export M2_HOME=/tmp/apache-maven-3.2.2 && export M2=\$M2_HOME/bin && export PATH=\$M2:\$PATH && mvn --version && mvn dependency:go-offline install && mvn --offline install"
buildah run $ctr_mq -- sh -c "cd /usr/src/mymaven && export M2_HOME=/tmp/apache-maven-3.6.0 && export M2=\$M2_HOME/bin && export PATH=\$M2:\$PATH && mvn --version && mvn dependency:go-offline install && mvn --offline install"
mkdir -p $mnt_mq/opt/app
@@ -53,13 +63,9 @@ cp $mnt_mq/usr/src/mymaven/target/lib/*.jar $mnt_mq/opt/app/
rm -rf $mnt_mq/tmp/*
rm -rf $mnt_mq/usr/src/mymaven
# We can't uninstall tar or gzip because they are required
buildah run $ctr_mq -- yum remove -y \
wget
# Clean up cached files
buildah run $ctr_mq -- yum clean all
rm -rf ${mnt_mq}/var/cache/yum/*
buildah run ${ctr_mq} -- microdnf ${microdnf_opts} clean all
rm -rf ${mnt_mq}/etc/yum.repos.d/*
###############################################################################
# Contain image finalization
@@ -69,6 +75,7 @@ buildah config \
--os linux \
--label architecture=x86_64 \
--label name="${imagename%:*}" \
--cmd "" \
--entrypoint '["java", "-classpath", "/opt/app/*", "org.junit.platform.console.ConsoleLauncher", "-p", "com.ibm.mqcontainer.test", "--details", "verbose"]' \
$ctr_mq
buildah unmount $ctr_mq

View File

@@ -32,19 +32,19 @@ limitations under the License.
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.2.0</version>
<version>5.3.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.2.0</version>
<version>5.3.2</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-console-standalone</artifactId>
<version>1.2.0</version>
<version>1.3.2</version>
<scope>runtime</scope>
</dependency>
</dependencies>

Binary file not shown.

View File

@@ -23,7 +23,7 @@ PASSWORD=passw0rd
openssl req \
-newkey rsa:2048 -nodes -keyout ${KEY} \
-subj "/CN=localhost" \
-x509 -days 365 -out ${CERT}
-x509 -days 3650 -out ${CERT}
# Add the key and certificate to a PKCS #12 key store, for the server to use
openssl pkcs12 \

View File

@@ -1,17 +1,17 @@
-----BEGIN CERTIFICATE-----
MIICpDCCAYwCCQDft9xlN4fNFTANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDDAls
b2NhbGhvc3QwHhcNMTgwMzIwMTUxODMwWhcNMTkwMzIwMTUxODMwWjAUMRIwEAYD
VQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDk
XzX0xQIZzKVX8/lDQh5lSHr5U9cBL+kURA3fEgl3ks9KjZPggfxWl4Y5dekChW/s
iknVssoNw9vI1W25qtQ81zRFQbHbpej0lLdYsS8/yZCuAVjMTp6Q9IswTwhVA6OD
5orag5dH3XQH+GsnmGXRCY7Gs93onAe3i3ShX9qpUFOJXyxCX+pLAC6kWQ3f/HI8
dujVXKsg1vHgOgGqQGwnh8gm5OeWUeuTMdD2v7Hn1OxilgNMbcewA7bpvipgm2xt
ZD0PKFDmtQ4comr25Oo+eUf1N7jSpRPOWJNxoyS9/coQUPp1Gpbk7khYHjGn7f5a
EZqQ4Hmwwh50uT+vKVxDAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAAHaywC7ZLOi
3PKlidj6PWe33dEVsDL6RRb3cOqR86Ld2aD91oLrpELRhz4v2mt/GfQMIg7rc6z7
26SuPzV/7zZAv1N/vGoIFyvBXWLYP5qCwUrmykcH/wfFM80S6FJxz5Wy5MA5UzTB
HdpiQCPu4U0IKgATLDraz0xlQ61Rog56YhgJI8ulHuav5iYxqV2mwU09Hs0kXPJ7
g0PLRaSyidsXafxBKukeM9QHl8z8HN8er23oqecYo59b/Bt0c6jSrJCK39EUcoLP
HxR+Ma1SPhVKGqa3lPmaoAzsFTqaJ6fsIcbp+oEFAq0LPeqMPK7u3ygT4iTblAl8
q3isCz4Ytx4=
MIICpDCCAYwCCQC6vpJFnfYO6TANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDDAls
b2NhbGhvc3QwHhcNMTkwMzIxMTYxMzUxWhcNMjkwMzE4MTYxMzUxWjAUMRIwEAYD
VQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCu
48qtIDwmihFqj2HY3dZjPfROA1MJ+D0c6aEA08ooOczthLB7XdZBQDapj8LFldyt
4ZMbTkqtF5QtPXmJY0wi39foLYlcGXPL1b7y3mypaFou88BcSM3VmfILKXhNeAlt
rXevnuT5kDU7sLVgKGhGwas20T1MU7d0I3bQ5z5c7egL76Hk9fYucjN6RkbwlrJ3
TrCXrGIziofn3Zq1t51ygv21c80JD3XJ44YmuCrede4rhOS/4NpwRuZyiwpJ6tlv
0L0QSDGCmt2JT3ty28UAsGznFzC5Qu9KyaR+9Gk4aftiyKxrYWZkgtJmMRU+C1X2
kFLOHsucGmJswjwubSR7AgMBAAEwDQYJKoZIhvcNAQELBQADggEBAEdlmXVGy86P
XIX5a4ZmHQ5Ns4wm7rY8vzUxlymEQ86En1PN1zAO9gV94tLyNeMptjsFEEo/uJhC
Yvg3l5TIr/WCiY2+2XsSHvnbXrlbF3S0fRHa9VaCMRKjzRT68uq2Y891906YGtUE
m6fCjHqVzX8qaplDf79aVkPydYaYOIZ1a/mCfQcD9XMZ/v5zI9IUDhdoq97bgPhB
gBOzWLI+hkzyU8jxKAFw1Hwi9lD/P6RXL5arNb/+arOgA3vTW+xGWGevgjVK1Ay9
81beWiQmn0KbeLZxj+WJ9Nntlf1M4EqPYgsSYs/IlJTYS8W1B0mDJEoovPdFTryY
GyIuQEVcjUE=
-----END CERTIFICATE-----

View File

@@ -1,28 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDkXzX0xQIZzKVX
8/lDQh5lSHr5U9cBL+kURA3fEgl3ks9KjZPggfxWl4Y5dekChW/siknVssoNw9vI
1W25qtQ81zRFQbHbpej0lLdYsS8/yZCuAVjMTp6Q9IswTwhVA6OD5orag5dH3XQH
+GsnmGXRCY7Gs93onAe3i3ShX9qpUFOJXyxCX+pLAC6kWQ3f/HI8dujVXKsg1vHg
OgGqQGwnh8gm5OeWUeuTMdD2v7Hn1OxilgNMbcewA7bpvipgm2xtZD0PKFDmtQ4c
omr25Oo+eUf1N7jSpRPOWJNxoyS9/coQUPp1Gpbk7khYHjGn7f5aEZqQ4Hmwwh50
uT+vKVxDAgMBAAECggEBAL91kybChCBdEcHLKQ7aP+FqAq9FOtwj7qSu6XI7DPTS
gDdgurleQM/X+Q/zaoZSmKMWzQ/79KnVqk2VoYgnUAgx5ACsMxCS59slUxFoetRf
iIxZVLj0sLuWSZsWp0We51eN0Juh9xKo9r435p4rhjDacnjkEwcQyOd4Yy9nzUpk
GDD5Vu1J9bOOKUQZ0qgjPyl/xWiwD1yfGJ0nHpQ5ucfrCO9p+n7SYsx01WcAkC8J
WP9XSXgi5uIefTWb/4m2b32jzjIgzAHkNx6yktRTjBJ7QILnKq1P8JjkNA/Awj4P
OxAz9hHHnVRuq4ZlEqfvo9p9YAbN2IH5TnmN3rGCXwECgYEA9JitVIeXCS0qIMFA
dKCmm9CT7JXccdpVllwaaYCNTb+G2RBrJqAvQEetoYJodWTIm1mNwSEORFFw0W+N
eaMzibJoJ+MZHRhiulDJaY0vwAKHkSJjDPJrPLgGMCUOLiWSAAnR4z35WfeY0e//
JbdZZemrJRyzy3o6rkRN9TQcUMUCgYEA7wTj5w5GZ8NQ7Nn8nIS2ayk+woIMHS+g
RVFufJoBeopsNJfNzGak0s+nz5q0nMGMzQsxXkbmAOLMTU3woQ7cEGjkLAfoch23
ACOe7M4rZbIk6kVNOlFESWdVdWViVd/B2a7oBqOIykoqX6VSqqrw+xghAUmd/2W1
uxjg9v01OWcCgYApE5LYRUUKF3mhspKeg3Q3apnM+4Xf4OjKrYEKArq4OdftkCJO
hEwrIV55Zysfu+Mso6d4rZJ1yq+FnJRHvy6ii0GOoUbQag36eCK7BSjluAcISpwT
yopT0hvH7hEpksmoE/4ZiYjcoQYbC5DvxpDO2qURQHa5TzeXmIT3Dt9KeQKBgQC6
UKeOXrRHAhs85ZdiMpk340jGujTTM2LNZfKoMixg5zH9tS9427IzmicHT2LmpoEo
/EaZZM65dhEnWU/vW/Py3rCuGeP5wGv8Mcgac4OknD7mVusiQGLojSIyhrsmkWs8
UnkPY76nYTSypd5Qpzt9n4tqw4XjpdcJZxVFso8glQKBgQCHlb15As73En/Q2AxL
5FY1Q1lLuO8y33ZZIRK4eynOKkbiuAh7X+ONZ4T9NtTm2J7mnltvTHZ7yeOI+VLS
LrTTBwnnNfdpp8UVPQlwzeizoDqSbr1sjFYvKOfdDDfxuzieT/4tfW9VTAxn4uOg
qpg7aRMUYUuLAH+S5atdOqXB+g==
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCu48qtIDwmihFq
j2HY3dZjPfROA1MJ+D0c6aEA08ooOczthLB7XdZBQDapj8LFldyt4ZMbTkqtF5Qt
PXmJY0wi39foLYlcGXPL1b7y3mypaFou88BcSM3VmfILKXhNeAltrXevnuT5kDU7
sLVgKGhGwas20T1MU7d0I3bQ5z5c7egL76Hk9fYucjN6RkbwlrJ3TrCXrGIziofn
3Zq1t51ygv21c80JD3XJ44YmuCrede4rhOS/4NpwRuZyiwpJ6tlv0L0QSDGCmt2J
T3ty28UAsGznFzC5Qu9KyaR+9Gk4aftiyKxrYWZkgtJmMRU+C1X2kFLOHsucGmJs
wjwubSR7AgMBAAECggEAH9t6teKjUlngJksMBdcTEGzerb9JRw2jBDtCisYJkx5E
SBfdlftX5fbufiCj2B4eXsYyZ8zxKWqcIUmLdA1Udx3TVIXG+bHhOAYtjEwb+xf5
JYhdR/IzHG+4eXQKaAIvpXztyl3lU9iC+eaMg4GYzRrGN2wSAG9XgZ5cLF2TLJYU
jPxp7goz9X6V57aL2G/EFlbFsMaI/6cW7+XoRdo0I4N2Z766gz7GgyxtTVwR5Peq
LjOpqSNS0W57KJxReURfySok9CP1DfyigopsYW8O4jGVDDRLdiN3I8+JhWya2E0j
96hHpN04Oz6HnMm7bdZDVtkZCOiu6xIzLJJxZ4o+kQKBgQDYqOA/hSod7s7w4LBE
A6Mp+e0//PYH6/N9SKmSIgQNec9bMGI4yanoblMbg4GM1g7pkvjlC0nTdjnUbLkB
vIvtVh3XwTIlrZ/4lc7VB23/hmKU+lRc+NJP5fgasAQu0W3+qp2cXo0pnHVwBEku
Z7FwDPX0JNDIi/Or2I7dt8JojQKBgQDOpU1AnIXv1/cToYK4nz8BWLxRxwLTxy5A
ucafNKacPlxb5luZRCExiPZwAM8Z3zI9o99rYXOPQmsnknZWJV66Zx0Vo0yTD1CT
DWMUj0ugI1wORNMhwZP6YBYWjAeupyU9a7FyU1Geg4sdQt5rMyAEQOoECc8x8foP
rySHuO/TJwKBgBjMM2ZxymFErQDa5rHSLMGoLmRtgodjlSnYwDfOluIn9/i67/MJ
+d11iyOSCKji8y/+t2gXw6plVLcgfohZWTaf7ah9H006sx2Tn+m4APoHGo9sm21M
uV2Vt7DuRnxJUiqcwo9cLxH9K1/Xzbx299MYWKpJ8G+TvR8FGUz9NE4dAoGAM5gs
KKSsAE1QwFMEG2qPRZvNMTHaL9w8XSbFQ7zWmI4tazihyCutifujZCWfj9sdZSyE
PQBQ5QT1UiUMbMfZ1fqm1V83YERjnsOp6Fk6zZnmgx2GBZiahNn2ydxekqni72nz
HRNWfphjZIPsmqFiLg2zIBz+4X6EK+RT35s6LeMCgYEAwF/9jX8kONW5KKZdoNHa
opkLpa9qkwTGQ9M3AZiRUjM4rtvggYt8FBEP+3BLDLHqfUOkPq82MCRXm+6Cz+sT
gyPnsPlAh/sr3Pys3olJbUDE9H24k1LU0CI/sSwAFkka0+Q7PVTTe/Dcavitrcrm
+fyiT2oSPZeHSjQE9iIW3OY=
-----END PRIVATE KEY-----

Binary file not shown.