Compare commits
93 Commits
v9.1.1
...
v9.1.2-UBI
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dc00231ec1 | ||
|
|
8a8ea6c6a9 | ||
|
|
9714d0d513 | ||
|
|
6691438fa1 | ||
|
|
426c6c3b9a | ||
|
|
8088cb2902 | ||
|
|
a2ce23aa96 | ||
|
|
95ba16cdb2 | ||
|
|
83fe77b222 | ||
|
|
7ab30723cd | ||
|
|
3f9fc0eaa5 | ||
|
|
ee4351e55d | ||
|
|
ebf55608d7 | ||
|
|
b3fd5f7562 | ||
|
|
3c9ec5f14c | ||
|
|
81c0b70a6f | ||
|
|
a6f307c6b5 | ||
|
|
6f677e2a59 | ||
|
|
612fe3a9ec | ||
|
|
0a9c745d96 | ||
|
|
b64c060ef4 | ||
|
|
8e22763f16 | ||
|
|
40b64e620e | ||
|
|
700cc53c07 | ||
|
|
44d75b169c | ||
|
|
c0bf371b9e | ||
|
|
e362644a55 | ||
|
|
c079c1b60d | ||
|
|
4a3bdf3b53 | ||
|
|
5ff269d2e3 | ||
|
|
3fb2d3fe61 | ||
|
|
6c72c894f7 | ||
|
|
63af43f19d | ||
|
|
64bb5aed8a | ||
|
|
f05a7d3eaf | ||
|
|
723fe2b998 | ||
|
|
44d0e0a432 | ||
|
|
33defc0fc9 | ||
|
|
d69befed71 | ||
|
|
0934289b61 | ||
|
|
f6231cd51c | ||
|
|
d5f04bc470 | ||
|
|
d8cbf4566e | ||
|
|
493bc7bfd4 | ||
|
|
1fa4f6f148 | ||
|
|
ce664dd654 | ||
|
|
f8e057a1d6 | ||
|
|
efd550822d | ||
|
|
d4df05fd2a | ||
|
|
194b04ac13 | ||
|
|
6848038165 | ||
|
|
0d3e177147 | ||
|
|
13f620f21a | ||
|
|
d4a81741cc | ||
|
|
0047301335 | ||
|
|
5ef532d2c1 | ||
|
|
281cdc4578 | ||
|
|
d68c051104 | ||
|
|
c5a52e616c | ||
|
|
c441de7d26 | ||
|
|
a194545f08 | ||
|
|
4f57d1bae2 | ||
|
|
cc0f072908 | ||
|
|
d834ac7c9c | ||
|
|
2dbee560fe | ||
|
|
17d3238161 | ||
|
|
c08ca2e79f | ||
|
|
84df0e8362 | ||
|
|
cc213f429f | ||
|
|
c29159dd38 | ||
|
|
f345ccf920 | ||
|
|
d1b1cfc5d8 | ||
|
|
a19c455ea4 | ||
|
|
694b31d6e8 | ||
|
|
33f82d76ff | ||
|
|
77319629fe | ||
|
|
d9c70c48c5 | ||
|
|
599f5f4b53 | ||
|
|
6840a575f9 | ||
|
|
1b8c816f57 | ||
|
|
9a8ff9b524 | ||
|
|
f3c858184f | ||
|
|
43676049b7 | ||
|
|
df6ce917c2 | ||
|
|
d3eb6e0d3d | ||
|
|
2bfdd51a01 | ||
|
|
be11b3cda1 | ||
|
|
525ff82fe7 | ||
|
|
3e07814bf6 | ||
|
|
b1daacf377 | ||
|
|
9c8b3825be | ||
|
|
4145f077b6 | ||
|
|
c063ddd67d |
25
.travis.yml
25
.travis.yml
@@ -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
|
||||
@@ -55,7 +61,8 @@ before_script:
|
||||
- make deps
|
||||
- echo -en 'travis_fold:end:deps\\r'
|
||||
- echo 'Building Developer image...' && echo -en 'travis_fold:start:build-devserver\\r'
|
||||
- make build-devserver
|
||||
# Use the containerized build explicitly
|
||||
- make build-devserver-ctr
|
||||
- echo -en 'travis_fold:end:build-devserver\\r'
|
||||
- echo 'Building Developer JMS test image...' && echo -en 'travis_fold:start:build-devjmstest\\r'
|
||||
- make build-devjmstest
|
||||
|
||||
25
CHANGELOG.md
25
CHANGELOG.md
@@ -1,11 +1,34 @@
|
||||
# Change log
|
||||
|
||||
## 9.1.2.0-UBI (2019-06-21)
|
||||
|
||||
**Breaking changes**:
|
||||
* UID of the mqm user is now 888. You need to run the container with an entrypoint of `runmqserver -i` under the root user to update any existing files.
|
||||
* MQSC files supplied will be verified before being run. Files containing invalid MQSC will cause the container to fail to start
|
||||
|
||||
**Other changes**:
|
||||
* Security fixes
|
||||
* Web console added to production image
|
||||
* Container built on RedHat host
|
||||
|
||||
## 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)))
|
||||
|
||||
@@ -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,23 +12,35 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# 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
|
||||
|
||||
###############################################################################
|
||||
# Build stage to build Go code
|
||||
###############################################################################
|
||||
FROM $BUILDER_IMAGE as builder
|
||||
WORKDIR /go/src/github.com/ibm-messaging/mq-container/
|
||||
FROM registry.access.redhat.com/devtools/go-toolset-7-rhel7 as builder
|
||||
# FROM docker.io/centos/go-toolset-7-centos7 as builder
|
||||
# The URL to download the MQ installer from in tar.gz format
|
||||
# This assumes an archive containing the MQ RPM install packages
|
||||
ARG MQ_URL="https://public.dhe.ibm.com/ibmdl/export/pub/software/websphere/messaging/mqadv/mqadv_dev912_linux_x86-64.tar.gz"
|
||||
ARG IMAGE_REVISION="Not specified"
|
||||
ARG IMAGE_SOURCE="Not specified"
|
||||
ARG IMAGE_TAG="Not specified"
|
||||
ARG MQM_UID=888
|
||||
USER 0
|
||||
COPY install-mq.sh /usr/local/bin/
|
||||
RUN chmod a+x /usr/local/bin/install-mq.sh \
|
||||
&& sleep 1 \
|
||||
&& MQ_PACKAGES="MQSeriesRuntime-*.rpm MQSeriesSDK-*.rpm MQSeriesSamples*.rpm" install-mq.sh $MQM_UID
|
||||
WORKDIR /opt/app-root/src/go/src/github.com/ibm-messaging/mq-container/
|
||||
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/
|
||||
ENV PATH="${PATH}:/opt/rh/go-toolset-7/root/usr/bin" \
|
||||
CGO_CFLAGS="-I/opt/mqm/inc/" \
|
||||
CGO_LDFLAGS_ALLOW="-Wl,-rpath.*"
|
||||
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
|
||||
RUN go build ./cmd/runmqdevserver/
|
||||
RUN go test -v ./cmd/runmqdevserver/...
|
||||
RUN go test -v ./cmd/runmqserver/
|
||||
RUN go test -v ./cmd/chkmqready/
|
||||
RUN go test -v ./cmd/chkmqhealthy/
|
||||
@@ -38,37 +50,93 @@ RUN go vet ./cmd/... ./internal/...
|
||||
###############################################################################
|
||||
# Main build stage, to build MQ image
|
||||
###############################################################################
|
||||
FROM $BASE_IMAGE
|
||||
|
||||
# The URL to download the MQ installer from in tar.gz format
|
||||
# This assumes an archive containing the MQ Debian (.deb) install packages
|
||||
ARG MQ_URL
|
||||
|
||||
FROM registry.access.redhat.com/ubi7/ubi-minimal AS mq-server
|
||||
# The MQ packages to install - see install-mq.sh for default value
|
||||
ARG MQ_PACKAGES
|
||||
|
||||
ARG MQ_URL="https://public.dhe.ibm.com/ibmdl/export/pub/software/websphere/messaging/mqadv/mqadv_dev912_linux_x86-64.tar.gz"
|
||||
ARG MQ_PACKAGES="MQSeriesRuntime-*.rpm MQSeriesServer-*.rpm MQSeriesJava*.rpm MQSeriesJRE*.rpm MQSeriesGSKit*.rpm MQSeriesMsg*.rpm MQSeriesSamples*.rpm MQSeriesWeb*.rpm MQSeriesAMS-*.rpm"
|
||||
#ARG MQ_PACKAGES="ibmmq-server ibmmq-java ibmmq-jre ibmmq-gskit ibmmq-msg-.* ibmmq-samples ibmmq-web ibmmq-ams"
|
||||
ARG MQM_UID=888
|
||||
LABEL summary="IBM MQ Advanced Server"
|
||||
LABEL description="Simplify, accelerate and facilitate the reliable exchange of data with a security-rich messaging solution — trusted by the world’s most successful enterprises"
|
||||
LABEL vendor="IBM"
|
||||
LABEL distribution-scope="private"
|
||||
LABEL authoritative-source-url="https://www.ibm.com/software/passportadvantage/"
|
||||
LABEL url="https://www.ibm.com/products/mq/advanced"
|
||||
LABEL io.openshift.tags="mq messaging"
|
||||
LABEL io.k8s.display-name="IBM MQ Advanced Server"
|
||||
LABEL io.k8s.description="Simplify, accelerate and facilitate the reliable exchange of data with a security-rich messaging solution — trusted by the world’s most successful enterprises"
|
||||
COPY install-mq.sh /usr/local/bin/
|
||||
|
||||
COPY install-mq-server-prereqs.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 \
|
||||
RUN env && chmod u+x /usr/local/bin/install-*.sh \
|
||||
&& sleep 1 \
|
||||
&& install-mq.sh
|
||||
|
||||
&& install-mq-server-prereqs.sh $MQM_UID \
|
||||
&& install-mq.sh $MQM_UID
|
||||
# Create a directory for runtime data from runmqserver
|
||||
RUN mkdir -p /run/runmqserver \
|
||||
&& chown mqm:mqm /run/runmqserver
|
||||
|
||||
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/chkmq* /usr/local/bin/
|
||||
COPY --from=builder /opt/app-root/src/go/src/github.com/ibm-messaging/mq-container/runmqserver /usr/local/bin/
|
||||
COPY --from=builder /opt/app-root/src/go/src/github.com/ibm-messaging/mq-container/chkmq* /usr/local/bin/
|
||||
COPY NOTICES.txt /opt/mqm/licenses/notices-container.txt
|
||||
|
||||
# Copy web XML files
|
||||
COPY web /etc/mqm/web
|
||||
COPY etc/mqm/*.tpl /etc/mqm/
|
||||
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* \
|
||||
&& chown -R mqm:mqm /etc/mqm/* \
|
||||
&& 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
|
||||
|
||||
EXPOSE 1414 9157 9443
|
||||
ENV LANG=en_US.UTF-8 AMQ_DIAGNOSTIC_MSG_SEVERITY=1 AMQ_ADDITIONAL_JSON_LOG=1 LOG_FORMAT=basic
|
||||
|
||||
USER $MQM_UID
|
||||
ENTRYPOINT ["runmqserver"]
|
||||
|
||||
###############################################################################
|
||||
# Add default developer config
|
||||
###############################################################################
|
||||
FROM mq-server AS mq-dev-server
|
||||
ARG MQM_UID=888
|
||||
# Enable MQ developer default configuration
|
||||
ENV MQ_DEV=true
|
||||
# Default administrator password
|
||||
ENV MQ_ADMIN_PASSWORD=passw0rd
|
||||
LABEL summary="IBM MQ Advanced for Developers Server"
|
||||
LABEL description="Simplify, accelerate and facilitate the reliable exchange of data with a security-rich messaging solution — trusted by the world’s most successful enterprises"
|
||||
LABEL vendor="IBM"
|
||||
LABEL distribution-scope="private"
|
||||
LABEL authoritative-source-url="https://www.ibm.com/software/passportadvantage/"
|
||||
LABEL url="https://www.ibm.com/products/mq/advanced"
|
||||
LABEL io.openshift.tags="mq messaging"
|
||||
LABEL io.k8s.display-name="IBM MQ Advanced for Developers Server"
|
||||
LABEL io.k8s.description="Simplify, accelerate and facilitate the reliable exchange of data with a security-rich messaging solution — trusted by the world’s most successful enterprises"
|
||||
USER 0
|
||||
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 \
|
||||
&& useradd app -G mqclient \
|
||||
&& echo admin:$MQ_ADMIN_PASSWORD | chpasswd
|
||||
# Create a directory for runtime data from runmqserver
|
||||
RUN mkdir -p /run/runmqdevserver \
|
||||
&& chown mqm:mqm /run/runmqdevserver
|
||||
COPY --from=builder /opt/app-root/src/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 chown -R mqm:mqm /etc/mqm/* \
|
||||
&& chmod +x /usr/local/bin/runmq* \
|
||||
&& install --directory --mode 0775 --owner mqm --group root /run/runmqdevserver
|
||||
ENV MQ_ENABLE_EMBEDDED_WEB_SERVER=1
|
||||
USER $MQM_UID
|
||||
ENTRYPOINT ["runmqdevserver"]
|
||||
|
||||
2
LICENSE
2
LICENSE
@@ -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.
|
||||
|
||||
344
Makefile
344
Makefile
@@ -1,4 +1,4 @@
|
||||
# © Copyright IBM Corporation 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.
|
||||
@@ -13,9 +13,63 @@
|
||||
# limitations under the License.
|
||||
|
||||
###############################################################################
|
||||
# Variables
|
||||
# Conditional variables - you can override the values of these variables from
|
||||
# the command line
|
||||
###############################################################################
|
||||
# MQ_VERSION is the fully qualified MQ version number to build
|
||||
MQ_VERSION ?= 9.1.2.0
|
||||
# RELEASE shows what release of the container code has been built
|
||||
RELEASE ?= 3
|
||||
# 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_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))
|
||||
# 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))
|
||||
# Options to `go test` for the Docker tests
|
||||
TEST_OPTS_DOCKER ?=
|
||||
# MQ_IMAGE_ADVANCEDSERVER is the name of the built MQ Advanced image
|
||||
MQ_IMAGE_ADVANCEDSERVER ?=mqadvanced-server
|
||||
# MQ_IMAGE_DEVSERVER is the name of the built MQ Advanced for Developers image
|
||||
MQ_IMAGE_DEVSERVER ?=mqadvanced-server-dev
|
||||
# MQ_TAG is the tag of the built MQ Advanced image & MQ Advanced for Developers image
|
||||
MQ_TAG ?=$(MQ_VERSION)-$(ARCH)
|
||||
# DOCKER is the Docker command to run. Defaults to "podman" if it's available, otherwise "docker"
|
||||
DOCKER ?= $(shell type -p podman || echo docker)
|
||||
# MQ_PACKAGES specifies the MQ packages (.deb or .rpm) to install. Defaults vary on base image.
|
||||
MQ_PACKAGES ?=MQSeriesRuntime-*.rpm MQSeriesServer-*.rpm MQSeriesJava*.rpm MQSeriesJRE*.rpm MQSeriesGSKit*.rpm MQSeriesMsg*.rpm MQSeriesSamples*.rpm MQSeriesWeb*.rpm MQSeriesAMS-*.rpm
|
||||
# MQM_UID is the UID to use for the "mqm" user
|
||||
MQM_UID ?= 888
|
||||
|
||||
###############################################################################
|
||||
# Other variables
|
||||
###############################################################################
|
||||
GO_PKG_DIRS = ./cmd ./internal ./test
|
||||
MQ_ARCHIVE_TYPE=LINUX
|
||||
MQ_ARCHIVE_DEV_PLATFORM=linux
|
||||
# ARCH is the platform architecture (e.g. amd64, ppc64le or s390x)
|
||||
ARCH=$(if $(findstring x86_64,$(shell uname -m)),amd64,$(shell uname -m))
|
||||
# BUILD_SERVER_CONTAINER is the name of the web server container used at build time
|
||||
BUILD_SERVER_CONTAINER=build-server
|
||||
# NUM_CPU is the number of CPUs available to Docker. Used to control how many
|
||||
# test run in parallel
|
||||
NUM_CPU = $(or $(shell docker info --format "{{ .NCPU }}"),2)
|
||||
# BASE_IMAGE_TAG is a normalized version of BASE_IMAGE, suitable for use in a Docker tag
|
||||
BASE_IMAGE_TAG=$(lastword $(subst /, ,$(subst :,-,$(BASE_IMAGE))))
|
||||
#BASE_IMAGE_TAG=$(subst /,-,$(subst :,-,$(BASE_IMAGE)))
|
||||
MQ_IMAGE_DEVSERVER_BASE=mqadvanced-server-dev-base
|
||||
# Docker image name to use for JMS tests
|
||||
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))))
|
||||
|
||||
# Set variable if running on a Red Hat Enterprise Linux host
|
||||
ifneq ($(wildcard /etc/redhat-release),)
|
||||
@@ -25,94 +79,245 @@ ifeq "$(findstring Red Hat,$(REDHAT_RELEASE))" "Red Hat"
|
||||
endif
|
||||
endif
|
||||
|
||||
ifneq (,$(findstring Microsoft,$(shell uname -r)))
|
||||
DOWNLOADS_DIR=$(patsubst /mnt/c%,C:%,$(realpath ./downloads/))
|
||||
else ifneq (,$(findstring Windows,$(shell echo ${OS})))
|
||||
DOWNLOADS_DIR=$(shell pwd)/downloads/
|
||||
else
|
||||
DOWNLOADS_DIR=$(realpath ./downloads/)
|
||||
endif
|
||||
|
||||
# Try to figure out which archive to use from the architecture
|
||||
ifeq "$(ARCH)" "amd64"
|
||||
MQ_ARCHIVE_ARCH=X86-64
|
||||
MQ_DEV_ARCH=x86-64
|
||||
else ifeq "$(ARCH)" "ppc64le"
|
||||
MQ_ARCHIVE_ARCH=LE_POWER
|
||||
MQ_DEV_ARCH=ppcle
|
||||
else ifeq "$(ARCH)" "s390x"
|
||||
MQ_ARCHIVE_ARCH=SYSTEM_Z
|
||||
MQ_DEV_ARCH=s390x
|
||||
endif
|
||||
# Archive names for IBM MQ Advanced for Developers
|
||||
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
|
||||
###############################################################################
|
||||
.PHONY: vars
|
||||
vars:
|
||||
@echo $(MQ_ARCHIVE_ARCH)
|
||||
@echo $(MQ_ARCHIVE_TYPE)
|
||||
@echo $(MQ_ARCHIVE)
|
||||
|
||||
# Targets default to a RHEL image on a RHEL host, or an Ubuntu image everywhere else
|
||||
.PHONY: default
|
||||
default: build-devserver test
|
||||
|
||||
.PHONY: build-devserver
|
||||
ifdef RHEL_HOST
|
||||
build-devserver: build-devserver-rhel
|
||||
else
|
||||
build-devserver: build-devserver-ubuntu
|
||||
endif
|
||||
# Build all components (except incubating ones)
|
||||
.PHONY: all
|
||||
all: build-devserver build-advancedserver
|
||||
|
||||
.PHONY: test-all
|
||||
test-all: build-devjmstest test-devserver test-advancedserver
|
||||
|
||||
.PHONY: devserver
|
||||
devserver: build-devserver build-devjmstest test-devserver
|
||||
|
||||
# Build incubating components
|
||||
.PHONY: incubating
|
||||
incubating: build-explorer
|
||||
|
||||
downloads/$(MQ_ARCHIVE_DEV):
|
||||
$(info $(SPACER)$(shell printf $(TITLE)"Downloading IBM MQ Advanced for Developers "$(MQ_VERSION)$(END)))
|
||||
mkdir -p downloads
|
||||
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)))
|
||||
mkdir -p downloads
|
||||
cd downloads; curl -LO https://public.dhe.ibm.com/ibmdl/export/pub/software/websphere/messaging/mqadv/$(MQ_SDK_ARCHIVE)
|
||||
|
||||
.PHONY: downloads
|
||||
downloads: downloads/$(MQ_ARCHIVE_DEV) downloads/$(MQ_SDK_ARCHIVE)
|
||||
|
||||
# Vendor Go dependencies for the Docker tests
|
||||
test/docker/vendor:
|
||||
cd test/docker && dep ensure -vendor-only
|
||||
|
||||
# Shortcut to just run the unit tests
|
||||
.PHONY: test-unit
|
||||
test-unit:
|
||||
docker build --target builder --file Dockerfile-server .
|
||||
|
||||
.PHONY: test-advancedserver
|
||||
test-advancedserver: test/docker/vendor
|
||||
$(info $(SPACER)$(shell printf $(TITLE)"Test $(MQ_IMAGE_ADVANCEDSERVER):$(MQ_TAG) on $(shell docker --version)"$(END)))
|
||||
docker inspect $(MQ_IMAGE_ADVANCEDSERVER):$(MQ_TAG)
|
||||
cd test/docker && TEST_IMAGE=$(MQ_IMAGE_ADVANCEDSERVER):$(MQ_TAG) EXPECTED_LICENSE=Production go test -parallel $(NUM_CPU) $(TEST_OPTS_DOCKER)
|
||||
|
||||
.PHONY: build-devjmstest
|
||||
build-devjmstest:
|
||||
$(info $(SPACER)$(shell printf $(TITLE)"Build JMS tests for developer config"$(END)))
|
||||
cd test/messaging && docker build --tag $(DEV_JMS_IMAGE) .
|
||||
|
||||
.PHONY: test-devserver
|
||||
test-devserver: test/docker/vendor
|
||||
$(info $(SPACER)$(shell printf $(TITLE)"Test $(MQ_IMAGE_DEVSERVER):$(MQ_TAG) on $(shell docker --version)"$(END)))
|
||||
docker inspect $(MQ_IMAGE_DEVSERVER):$(MQ_TAG)
|
||||
cd test/docker && TEST_IMAGE=$(MQ_IMAGE_DEVSERVER):$(MQ_TAG) EXPECTED_LICENSE=Developer DEV_JMS_IMAGE=$(DEV_JMS_IMAGE) IBMJRE=true go test -parallel $(NUM_CPU) -tags mqdev $(TEST_OPTS_DOCKER)
|
||||
|
||||
.PHONY: coverage
|
||||
coverage:
|
||||
mkdir coverage
|
||||
|
||||
.PHONY: test-advancedserver-cover
|
||||
test-advancedserver-cover: test/docker/vendor coverage
|
||||
$(info $(SPACER)$(shell printf $(TITLE)"Test $(MQ_IMAGE_ADVANCEDSERVER):$(MQ_TAG) with code coverage on $(shell docker --version)"$(END)))
|
||||
rm -f ./coverage/unit*.cov
|
||||
# Run unit tests with coverage, for each package under 'internal'
|
||||
go list -f '{{.Name}}' ./internal/... | xargs -I {} go test -cover -covermode count -coverprofile ./coverage/unit-{}.cov ./internal/{}
|
||||
# ls -1 ./cmd | xargs -I {} go test -cover -covermode count -coverprofile ./coverage/unit-{}.cov ./cmd/{}/...
|
||||
echo 'mode: count' > ./coverage/unit.cov
|
||||
tail -q -n +2 ./coverage/unit-*.cov >> ./coverage/unit.cov
|
||||
go tool cover -html=./coverage/unit.cov -o ./coverage/unit.html
|
||||
|
||||
rm -f ./test/docker/coverage/*.cov
|
||||
rm -f ./coverage/docker.*
|
||||
mkdir -p ./test/docker/coverage/
|
||||
cd test/docker && TEST_IMAGE=$(MQ_IMAGE_ADVANCEDSERVER):$(MQ_TAG)-cover TEST_COVER=true go test $(TEST_OPTS_DOCKER)
|
||||
echo 'mode: count' > ./coverage/docker.cov
|
||||
tail -q -n +2 ./test/docker/coverage/*.cov >> ./coverage/docker.cov
|
||||
go tool cover -html=./coverage/docker.cov -o ./coverage/docker.html
|
||||
|
||||
echo 'mode: count' > ./coverage/combined.cov
|
||||
tail -q -n +2 ./coverage/unit.cov ./coverage/docker.cov >> ./coverage/combined.cov
|
||||
go tool cover -html=./coverage/combined.cov -o ./coverage/combined.html
|
||||
|
||||
define build-mq
|
||||
# Create a temporary network to use for the build
|
||||
$(DOCKER) network create build
|
||||
# Start a web server to host the MQ downloadable (tar.gz) file
|
||||
$(DOCKER) run \
|
||||
--rm \
|
||||
--name $(BUILD_SERVER_CONTAINER) \
|
||||
--network build \
|
||||
--network-alias build \
|
||||
--volume $(DOWNLOADS_DIR):/usr/share/nginx/html:ro \
|
||||
--detach \
|
||||
docker.io/nginx:alpine
|
||||
# Build the new image
|
||||
$(DOCKER) build \
|
||||
--tag $1:$2 \
|
||||
--file $3 \
|
||||
--network build \
|
||||
--build-arg MQ_URL=http://build:80/$4 \
|
||||
--build-arg MQ_PACKAGES="$(MQ_PACKAGES)" \
|
||||
--build-arg IMAGE_REVISION="$(IMAGE_REVISION)" \
|
||||
--build-arg IMAGE_SOURCE="$(IMAGE_SOURCE)" \
|
||||
--build-arg IMAGE_TAG="$1:$2" \
|
||||
--build-arg MQM_UID=$(MQM_UID) \
|
||||
--label version=$(MQ_VERSION) \
|
||||
--label name=$1 \
|
||||
--label build-date=$(shell date +%Y-%m-%dT%H:%M:%S%z) \
|
||||
--label release="$(RELEASE)" \
|
||||
--label architecture="$(ARCH)" \
|
||||
--label run="docker run -d -e LICENSE=accept $1:$2" \
|
||||
--label vcs-ref=$(IMAGE_REVISION) \
|
||||
--label vcs-type=git \
|
||||
--label vcs-url=$(IMAGE_SOURCE) \
|
||||
--target $5 \
|
||||
. ; $(DOCKER) kill $(BUILD_SERVER_CONTAINER) && $(DOCKER) network rm build
|
||||
endef
|
||||
|
||||
define build-mq-ctr
|
||||
buildah/mq-buildah $1 $2 \
|
||||
--file /src/Dockerfile-server \
|
||||
--build-arg MQ_URL="file:///src/downloads/$3" \
|
||||
--build-arg MQ_PACKAGES="$(MQ_PACKAGES)" \
|
||||
--build-arg IMAGE_REVISION="$(IMAGE_REVISION)" \
|
||||
--build-arg IMAGE_SOURCE="$(IMAGE_SOURCE)" \
|
||||
--build-arg IMAGE_TAG="$1:$2" \
|
||||
--build-arg MQM_UID=$(MQM_UID) \
|
||||
--label version=$(MQ_VERSION) \
|
||||
--label name=$1 \
|
||||
--label build-date=$(shell date +%Y-%m-%dT%H:%M:%S%z) \
|
||||
--label release="$(RELEASE)" \
|
||||
--label architecture="$(ARCH)" \
|
||||
--label run="docker run -d -e LICENSE=accept $1:$2" \
|
||||
--label vcs-ref=$(IMAGE_REVISION) \
|
||||
--label vcs-type=git \
|
||||
--label vcs-url=$(IMAGE_SOURCE) \
|
||||
--target $4
|
||||
endef
|
||||
|
||||
DOCKER_SERVER_VERSION=$(shell docker version --format "{{ .Server.Version }}")
|
||||
DOCKER_CLIENT_VERSION=$(shell docker version --format "{{ .Client.Version }}")
|
||||
.PHONY: docker-version
|
||||
docker-version:
|
||||
@test "$(word 1,$(subst ., ,$(DOCKER_CLIENT_VERSION)))" -ge "17" || ("$(word 1,$(subst ., ,$(DOCKER_CLIENT_VERSION)))" -eq "17" && "$(word 2,$(subst ., ,$(DOCKER_CLIENT_VERSION)))" -ge "05") || (echo "Error: Docker client 17.05 or greater is required" && exit 1)
|
||||
@test "$(word 1,$(subst ., ,$(DOCKER_SERVER_VERSION)))" -ge "17" || ("$(word 1,$(subst ., ,$(DOCKER_SERVER_VERSION)))" -eq "17" && "$(word 2,$(subst ., ,$(DOCKER_CLIENT_VERSION)))" -ge "05") || (echo "Error: Docker server 17.05 or greater is required" && exit 1)
|
||||
|
||||
.PHONY: build-advancedserver
|
||||
ifdef RHEL_HOST
|
||||
build-advancedserver: build-advancedserver-rhel
|
||||
# Build using Buildah inside a container on RHEL hosts
|
||||
build-advancedserver: build-advancedserver-ctr
|
||||
else
|
||||
build-advancedserver: build-advancedserver-ubuntu
|
||||
build-advancedserver: build-advancedserver-host
|
||||
endif
|
||||
|
||||
.PHONY: build-advancedserver-host
|
||||
build-advancedserver-host: downloads/$(MQ_ARCHIVE) docker-version
|
||||
$(info $(SPACER)$(shell printf $(TITLE)"Build $(MQ_IMAGE_ADVANCEDSERVER):$(MQ_TAG)"$(END)))
|
||||
$(call build-mq,$(MQ_IMAGE_ADVANCEDSERVER),$(MQ_TAG),Dockerfile-server,$(MQ_ARCHIVE),mq-server)
|
||||
|
||||
.PHONY: test-devserver
|
||||
.PHONY: build-advancedserver-ctr
|
||||
build-advancedserver-ctr: downloads/$(MQ_ARCHIVE)
|
||||
$(info $(shell printf $(TITLE)"Build $(MQ_IMAGE_ADVANCEDSERVER):$(MQ_TAG) in a container"$(END)))
|
||||
$(call build-mq-ctr,$(MQ_IMAGE_ADVANCEDSERVER),$(MQ_TAG),$(MQ_ARCHIVE),mq-server)
|
||||
|
||||
.PHONY: build-devserver
|
||||
ifdef RHEL_HOST
|
||||
test-devserver: test-devserver-rhel
|
||||
# Build using Buildah inside a container on RHEL hosts
|
||||
build-devserver: build-devserver-ctr
|
||||
else
|
||||
test-devserver: test-devserver-ubuntu
|
||||
build-devserver: build-devserver-host
|
||||
endif
|
||||
|
||||
.PHONY: test-advancedserver
|
||||
ifdef RHEL_HOST
|
||||
test-advancedserver: test-advancedserver-rhel
|
||||
else
|
||||
test-advancedserver: test-advancedserver-ubuntu
|
||||
endif
|
||||
.PHONY: build-devserver-host
|
||||
build-devserver-host: downloads/$(MQ_ARCHIVE_DEV) docker-version
|
||||
$(info $(shell printf $(TITLE)"Build $(MQ_IMAGE_DEVSERVER):$(MQ_TAG)"$(END)))
|
||||
$(call build-mq,$(MQ_IMAGE_DEVSERVER),$(MQ_TAG),Dockerfile-server,$(MQ_ARCHIVE_DEV),mq-dev-server)
|
||||
|
||||
.PHONY: build-devjmstest
|
||||
ifdef RHEL_HOST
|
||||
build-devjmstest: build-devjmstest-rhel
|
||||
else
|
||||
build-devjmstest: build-devjmstest-ubuntu
|
||||
endif
|
||||
.PHONY: build-devserver-ctr
|
||||
build-devserver-ctr: downloads/$(MQ_ARCHIVE_DEV)
|
||||
$(info $(shell printf $(TITLE)"Build $(MQ_IMAGE_DEVSERVER):$(MQ_TAG) in a container"$(END)))
|
||||
$(call build-mq-ctr,$(MQ_IMAGE_DEVSERVER),$(MQ_TAG),$(MQ_ARCHIVE_DEV),mq-dev-server)
|
||||
|
||||
# UBUNTU building targets
|
||||
.PHONY: build-devserver-ubuntu
|
||||
build-devserver-ubuntu:
|
||||
$(MAKE) -f Makefile-UBUNTU build-devserver
|
||||
.PHONY: build-advancedserver-cover
|
||||
build-advancedserver-cover: docker-version
|
||||
$(DOCKER) build --build-arg BASE_IMAGE=$(MQ_IMAGE_ADVANCEDSERVER):$(MQ_TAG) -t $(MQ_IMAGE_ADVANCEDSERVER):$(MQ_TAG)-cover -f Dockerfile-server.cover .
|
||||
|
||||
.PHONY: test-devserver-ubuntu
|
||||
test-devserver-ubuntu:
|
||||
$(MAKE) -f Makefile-UBUNTU test-devserver
|
||||
.PHONY: build-explorer
|
||||
build-explorer: downloads/$(MQ_ARCHIVE_DEV)
|
||||
$(call build-mq,mq-explorer,latest-$(ARCH),incubating/mq-explorer/Dockerfile,$(MQ_ARCHIVE_DEV),mq-explorer)
|
||||
|
||||
.PHONY: build-devjmstest-ubuntu
|
||||
$(MAKE) -f Makefile-UBUNTU build-devjmstest
|
||||
.PHONY: build-sdk
|
||||
build-sdk: downloads/$(MQ_ARCHIVE_DEV)
|
||||
$(info $(shell printf $(TITLE)"Build $(MQ_IMAGE_SDK)"$(END)))
|
||||
$(call build-mq,mq-sdk,$(MQ_TAG),incubating/mq-sdk/Dockerfile,$(MQ_SDK_ARCHIVE),mq-sdk)
|
||||
|
||||
.PHONY: build-advancedserver-ubuntu
|
||||
build-advancedserver-ubuntu:
|
||||
$(MAKE) -f Makefile-UBUNTU build-advancedserver
|
||||
.PHONY: debug-vars
|
||||
debug-vars:
|
||||
@echo MQ_VERSION=$(MQ_VERSION)
|
||||
@echo MQ_VERSION_VRM=$(MQ_VERSION_VRM)
|
||||
@echo MQ_ARCHIVE=$(MQ_ARCHIVE)
|
||||
@echo MQ_IMAGE_DEVSERVER=$(MQ_IMAGE_DEVSERVER)
|
||||
@echo MQ_IMAGE_ADVANCEDSERVER=$(MQ_IMAGE_ADVANCEDSERVER)
|
||||
|
||||
.PHONY: test-advancedserver-ubuntu
|
||||
test-advancedserver-ubuntu:
|
||||
$(MAKE) -f Makefile-UBUNTU test-advancedserver
|
||||
include formatting.mk
|
||||
|
||||
.PHONY: build-devjmstest-ubuntu
|
||||
build-devjmstest-ubuntu:
|
||||
$(MAKE) -f Makefile-UBUNTU build-devjmstest
|
||||
|
||||
# RHEL building targets
|
||||
.PHONY: build-devserver-rhel
|
||||
build-devserver-rhel:
|
||||
$(MAKE) -f Makefile-RHEL build-devserver
|
||||
|
||||
.PHONY: test-devserver-rhel
|
||||
test-devserver-rhel:
|
||||
$(MAKE) -f Makefile-RHEL test-devserver
|
||||
|
||||
.PHONY: build-advancedserver-rhel
|
||||
build-advancedserver-rhel:
|
||||
$(MAKE) -f Makefile-RHEL build-advancedserver
|
||||
|
||||
.PHONY: test-advancedserver-rhel
|
||||
test-advancedserver-rhel:
|
||||
$(MAKE) -f Makefile-RHEL test-advancedserver
|
||||
|
||||
.PHONY: build-devjmstest-rhel
|
||||
build-devjmstest-rhel:
|
||||
$(MAKE) -f Makefile-RHEL build-devjmstest
|
||||
|
||||
# Common targets
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -rf ./coverage
|
||||
@@ -167,9 +372,4 @@ gosec: $(info $(SPACER)$(shell printf "Running gosec test"$(END)))
|
||||
printf "\ngosec found no LOW severity issues\n" ;\
|
||||
fi ;\
|
||||
|
||||
.PHONY: unknownos
|
||||
unknownos:
|
||||
$(info $(SPACER)$(shell printf "ERROR: Unknown OS ("$(BASE_OS)") please run specific make targets"$(END)))
|
||||
exit 1
|
||||
|
||||
include formatting.mk
|
||||
|
||||
197
Makefile-RHEL
197
Makefile-RHEL
@@ -1,197 +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.
|
||||
|
||||
###############################################################################
|
||||
# Conditional variables - you can override the values of these variables from
|
||||
# the command line
|
||||
###############################################################################
|
||||
# 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_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_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))
|
||||
# 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
|
||||
MQ_IMAGE_ADVANCEDSERVER ?=mqadvanced-server:$(MQ_VERSION)-RHEL-$(ARCH)
|
||||
# MQ_IMAGE_DEVSERVER is the name and tag of the built MQ Advanced for Developers image
|
||||
MQ_IMAGE_DEVSERVER ?=mqadvanced-server-dev:$(MQ_VERSION)-RHEL-$(ARCH)
|
||||
# MQ_IMAGE_SDK is the name and tag of the built MQ Advanced for Developers SDK image
|
||||
MQ_IMAGE_SDK ?=mq-sdk:$(MQ_VERSION)-$(ARCH)-$(BASE_IMAGE_TAG)
|
||||
# MQ_IMAGE_GOLANG_SDK is the name and tag of the built MQ Advanced for Developers SDK image, plus Go tools
|
||||
MQ_IMAGE_GOLANG_SDK ?=mq-golang-sdk:$(MQ_VERSION)-$(ARCH)-$(BASE_IMAGE_TAG)
|
||||
# MQ_PACKAGES specifies the MQ packages to install. Defaults vary on base image.
|
||||
MQ_PACKAGES ?= MQSeriesRuntime-*.rpm MQSeriesServer-*.rpm MQSeriesJava*.rpm MQSeriesJRE*.rpm MQSeriesGSKit*.rpm MQSeriesMsg*.rpm MQSeriesSamples*.rpm MQSeriesAMS-*.rpm
|
||||
|
||||
###############################################################################
|
||||
# Other variables
|
||||
###############################################################################
|
||||
# ARCH is the platform architecture (e.g. x86_64, ppc64le or s390x)
|
||||
ARCH = $(shell uname -m)
|
||||
# BASE_IMAGE_TAG is a normalized version of BASE_IMAGE, suitable for use in a Docker tag
|
||||
BASE_IMAGE_TAG=$(subst /,-,$(subst :,-,$(BASE_IMAGE)))
|
||||
MQ_IMAGE_DEVSERVER_BASE=mqadvanced-server-dev-base:$(MQ_VERSION)-$(ARCH)-$(BASE_IMAGE_TAG)
|
||||
# Docker image name to use for JMS tests
|
||||
DEV_JMS_IMAGE=mq-dev-jms-test:latest
|
||||
# Variables for versioning
|
||||
IMAGE_REVISION=$(shell git rev-parse HEAD)
|
||||
IMAGE_SOURCE=$(shell git config --get remote.origin.url)
|
||||
MQDEV=
|
||||
|
||||
|
||||
ifneq (,$(findstring Microsoft,$(shell uname -r)))
|
||||
DOWNLOADS_DIR=$(patsubst /mnt/c%,C:%,$(realpath ./downloads/))
|
||||
else
|
||||
DOWNLOADS_DIR=$(realpath ./downloads/)
|
||||
endif
|
||||
|
||||
# Try to figure out which archive to use from the architecture
|
||||
ifeq "$(ARCH)" "x86_64"
|
||||
MQ_ARCHIVE_ARCH=X86-64
|
||||
MQ_DEV_ARCH=x86-64
|
||||
else ifeq "$(ARCH)" "ppc64le"
|
||||
MQ_ARCHIVE_ARCH=LE_POWER
|
||||
MQ_DEV_ARCH=ppcle
|
||||
else ifeq "$(ARCH)" "s390x"
|
||||
MQ_ARCHIVE_ARCH=SYSTEM_Z
|
||||
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
|
||||
|
||||
###############################################################################
|
||||
# Build targets
|
||||
###############################################################################
|
||||
.PHONY: vars
|
||||
vars:
|
||||
#ifeq "$(findstring ubuntu,$(BASE_IMAGE))","ubuntu"
|
||||
@echo $(MQ_ARCHIVE_ARCH)
|
||||
@echo $(MQ_ARCHIVE_TYPE)
|
||||
@echo $(MQ_ARCHIVE)
|
||||
|
||||
.PHONY: default
|
||||
default: build-devserver test-devserver
|
||||
|
||||
# Build all components (except incubating ones)
|
||||
.PHONY: all
|
||||
all: build-devserver build-advancedserver
|
||||
|
||||
.PHONY: test-all
|
||||
test-all: build-devjmstest test-devserver test-advancedserver
|
||||
|
||||
.PHONY: devserver
|
||||
devserver: build-devserver build-devjmstest test-devserver
|
||||
|
||||
# Build incubating components
|
||||
.PHONY: incubating
|
||||
incubating: build-explorer
|
||||
|
||||
downloads/$(MQ_ARCHIVE_DEV):
|
||||
$(info $(SPACER)$(shell printf $(TITLE)"Downloading IBM MQ Advanced for Developers "$(MQ_VERSION)$(END)))
|
||||
mkdir -p downloads
|
||||
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)))
|
||||
mkdir -p downloads
|
||||
cd downloads; curl -LO https://public.dhe.ibm.com/ibmdl/export/pub/software/websphere/messaging/mqadv/$(MQ_SDK_ARCHIVE)
|
||||
|
||||
.PHONY: downloads
|
||||
downloads: downloads/$(MQ_ARCHIVE_DEV) downloads/$(MQ_SDK_ARCHIVE)
|
||||
|
||||
# Vendor Go dependencies for the Docker tests
|
||||
test/docker/vendor:
|
||||
cd test/docker && dep ensure -vendor-only
|
||||
|
||||
|
||||
.PHONY: check-prereqs
|
||||
check-prereqs:
|
||||
$(info $(SPACER)$(shell printf $(TITLE)"Checking for prereqs"$(END)))
|
||||
which buildah || (echo "Missing required program buildah" && exit 1)
|
||||
which podman || (echo "Missing required program podman" && exit 1)
|
||||
yum list | grep yum-utils || (echo "Missing required package yum-utils" && exit 1)
|
||||
|
||||
.PHONY: check-test-prereqs
|
||||
check-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)
|
||||
|
||||
.PHONY: test-advancedserver
|
||||
test-advancedserver: check-test-prereqs test/docker/vendor
|
||||
$(info $(SPACER)$(shell printf $(TITLE)"Test $(MQ_IMAGE_ADVANCEDSERVER) on $(shell docker --version)"$(END)))
|
||||
sudo buildah push $(MQ_IMAGE_ADVANCEDSERVER) docker-daemon:$(MQ_IMAGE_ADVANCEDSERVER)
|
||||
docker tag docker.io/$(MQ_IMAGE_ADVANCEDSERVER) $(MQ_IMAGE_ADVANCEDSERVER)
|
||||
cd test/docker && TEST_IMAGE=$(MQ_IMAGE_ADVANCEDSERVER) EXPECTED_LICENSE=Production go test $(TEST_OPTS_DOCKER)
|
||||
|
||||
|
||||
.PHONY: test-devserver
|
||||
test-devserver: check-test-prereqs test/docker/vendor
|
||||
$(info $(SPACER)$(shell printf $(TITLE)"Test $(MQ_IMAGE_DEVSERVER) on $(shell docker --version)"$(END)))
|
||||
sudo buildah push $(MQ_IMAGE_DEVSERVER) docker-daemon:$(MQ_IMAGE_DEVSERVER)
|
||||
docker tag docker.io/$(MQ_IMAGE_DEVSERVER) $(MQ_IMAGE_DEVSERVER)
|
||||
cd test/docker && TEST_IMAGE=$(MQ_IMAGE_DEVSERVER) EXPECTED_LICENSE=Developer DEV_JMS_IMAGE=$(DEV_JMS_IMAGE) go test -tags mqdev $(TEST_OPTS_DOCKER)
|
||||
|
||||
|
||||
.PHONY: build-advancedserver
|
||||
build-advancedserver: MQ_SDK_ARCHIVE=$(MQ_ARCHIVE)
|
||||
build-advancedserver: check-prereqs downloads/$(MQ_ARCHIVE) build-go-programs-ex
|
||||
$(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
|
||||
$(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:
|
||||
$(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
|
||||
$(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)"
|
||||
|
||||
.PHONY: build-devjmstest
|
||||
build-devjmstest: check-test-prereqs
|
||||
$(info $(SPACER)$(shell printf $(TITLE)"Build JMS tests for developer config"$(END)))
|
||||
cd test/messaging && sudo ./buildah.sh $(DEV_JMS_IMAGE)
|
||||
sudo buildah push $(DEV_JMS_IMAGE) docker-daemon:$(DEV_JMS_IMAGE)
|
||||
docker tag docker.io/$(DEV_JMS_IMAGE) $(DEV_JMS_IMAGE)
|
||||
|
||||
include formatting.mk
|
||||
273
Makefile-UBUNTU
273
Makefile-UBUNTU
@@ -1,273 +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.
|
||||
|
||||
###############################################################################
|
||||
# Conditional variables - you can override the values of these variables from
|
||||
# the command line
|
||||
###############################################################################
|
||||
# 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_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_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))
|
||||
# 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
|
||||
MQ_IMAGE_ADVANCEDSERVER ?=mqadvanced-server:$(MQ_VERSION)-$(ARCH)-$(BASE_IMAGE_TAG)
|
||||
# MQ_IMAGE_DEVSERVER is the name and tag of the built MQ Advanced for Developers image
|
||||
MQ_IMAGE_DEVSERVER ?=mqadvanced-server-dev:$(MQ_VERSION)-$(ARCH)-$(BASE_IMAGE_TAG)
|
||||
# MQ_IMAGE_SDK is the name and tag of the built MQ Advanced for Developers SDK image
|
||||
MQ_IMAGE_SDK ?=mq-sdk:$(MQ_VERSION)-$(ARCH)-$(BASE_IMAGE_TAG)
|
||||
# MQ_IMAGE_GOLANG_SDK is the name and tag of the built MQ Advanced for Developers SDK image, plus Go tools
|
||||
MQ_IMAGE_GOLANG_SDK ?=mq-golang-sdk:$(MQ_VERSION)-$(ARCH)-$(BASE_IMAGE_TAG)
|
||||
# DOCKER is the Docker command to run
|
||||
DOCKER ?= docker
|
||||
# MQ_PACKAGES specifies the MQ packages (.deb or .rpm) to install. Defaults vary on base image.
|
||||
MQ_PACKAGES ?=
|
||||
|
||||
###############################################################################
|
||||
# Other variables
|
||||
###############################################################################
|
||||
# ARCH is the platform architecture (e.g. x86_64, ppc64le or s390x)
|
||||
ARCH = $(shell uname -m)
|
||||
# BUILD_SERVER_CONTAINER is the name of the web server container used at build time
|
||||
BUILD_SERVER_CONTAINER=build-server
|
||||
# NUM_CPU is the number of CPUs available to Docker. Used to control how many
|
||||
# test run in parallel
|
||||
NUM_CPU = $(or $(shell docker info --format "{{ .NCPU }}"),2)
|
||||
# BASE_IMAGE_TAG is a normalized version of BASE_IMAGE, suitable for use in a Docker tag
|
||||
BASE_IMAGE_TAG=$(subst /,-,$(subst :,-,$(BASE_IMAGE)))
|
||||
MQ_IMAGE_DEVSERVER_BASE=mqadvanced-server-dev-base:$(MQ_VERSION)-$(ARCH)-$(BASE_IMAGE_TAG)
|
||||
# Docker image name to use for JMS tests
|
||||
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)
|
||||
|
||||
ifneq (,$(findstring Microsoft,$(shell uname -r)))
|
||||
DOWNLOADS_DIR=$(patsubst /mnt/c%,C:%,$(realpath ./downloads/))
|
||||
else
|
||||
DOWNLOADS_DIR=$(realpath ./downloads/)
|
||||
endif
|
||||
|
||||
# Try to figure out which archive to use from the BASE_IMAGE
|
||||
ifeq "$(findstring ubuntu,$(BASE_IMAGE))" "ubuntu"
|
||||
MQ_ARCHIVE_TYPE=UBUNTU
|
||||
MQ_ARCHIVE_DEV_PLATFORM=ubuntu
|
||||
else
|
||||
MQ_ARCHIVE_TYPE=LINUX
|
||||
MQ_ARCHIVE_DEV_PLATFORM=linux
|
||||
endif
|
||||
# Try to figure out which archive to use from the architecture
|
||||
ifeq "$(ARCH)" "x86_64"
|
||||
MQ_ARCHIVE_ARCH=X86-64
|
||||
MQ_DEV_ARCH=x86-64
|
||||
else ifeq "$(ARCH)" "ppc64le"
|
||||
MQ_ARCHIVE_ARCH=LE_POWER
|
||||
MQ_DEV_ARCH=ppcle
|
||||
else ifeq "$(ARCH)" "s390x"
|
||||
MQ_ARCHIVE_ARCH=SYSTEM_Z
|
||||
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
|
||||
|
||||
###############################################################################
|
||||
# Build targets
|
||||
###############################################################################
|
||||
.PHONY: vars
|
||||
vars:
|
||||
#ifeq "$(findstring ubuntu,$(BASE_IMAGE))","ubuntu"
|
||||
@echo $(MQ_ARCHIVE_ARCH)
|
||||
@echo $(MQ_ARCHIVE_TYPE)
|
||||
@echo $(MQ_ARCHIVE)
|
||||
|
||||
.PHONY: default
|
||||
default: build-devserver test
|
||||
|
||||
# Build all components (except incubating ones)
|
||||
.PHONY: all
|
||||
all: build-devserver build-advancedserver
|
||||
|
||||
.PHONY: test-all
|
||||
test-all: build-devjmstest test-devserver test-advancedserver
|
||||
|
||||
.PHONY: devserver
|
||||
devserver: build-devserver build-devjmstest test-devserver
|
||||
|
||||
# Build incubating components
|
||||
.PHONY: incubating
|
||||
incubating: build-explorer
|
||||
|
||||
downloads/$(MQ_ARCHIVE_DEV):
|
||||
$(info $(SPACER)$(shell printf $(TITLE)"Downloading IBM MQ Advanced for Developers "$(MQ_VERSION)$(END)))
|
||||
mkdir -p downloads
|
||||
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)))
|
||||
mkdir -p downloads
|
||||
cd downloads; curl -LO https://public.dhe.ibm.com/ibmdl/export/pub/software/websphere/messaging/mqadv/$(MQ_SDK_ARCHIVE)
|
||||
|
||||
.PHONY: downloads
|
||||
downloads: downloads/$(MQ_ARCHIVE_DEV) downloads/$(MQ_SDK_ARCHIVE)
|
||||
|
||||
# Vendor Go dependencies for the Docker tests
|
||||
test/docker/vendor:
|
||||
cd test/docker && dep ensure -vendor-only
|
||||
|
||||
# Shortcut to just run the unit tests
|
||||
.PHONY: test-unit
|
||||
test-unit:
|
||||
docker build --target builder --file Dockerfile-server .
|
||||
|
||||
.PHONY: test-advancedserver
|
||||
test-advancedserver: test/docker/vendor
|
||||
$(info $(SPACER)$(shell printf $(TITLE)"Test $(MQ_IMAGE_ADVANCEDSERVER) on $(shell docker --version)"$(END)))
|
||||
cd test/docker && TEST_IMAGE=$(MQ_IMAGE_ADVANCEDSERVER) EXPECTED_LICENSE=Production go test -parallel $(NUM_CPU) $(TEST_OPTS_DOCKER)
|
||||
|
||||
.PHONY: build-devjmstest
|
||||
build-devjmstest:
|
||||
$(info $(SPACER)$(shell printf $(TITLE)"Build JMS tests for developer config"$(END)))
|
||||
cd test/messaging && docker build --tag $(DEV_JMS_IMAGE) .
|
||||
|
||||
.PHONY: test-devserver
|
||||
test-devserver: test/docker/vendor
|
||||
$(info $(SPACER)$(shell printf $(TITLE)"Test $(MQ_IMAGE_DEVSERVER) on $(shell docker --version)"$(END)))
|
||||
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:
|
||||
mkdir coverage
|
||||
|
||||
.PHONY: test-advancedserver-cover
|
||||
test-advancedserver-cover: test/docker/vendor coverage
|
||||
$(info $(SPACER)$(shell printf $(TITLE)"Test $(MQ_IMAGE_ADVANCEDSERVER) with code coverage on $(shell docker --version)"$(END)))
|
||||
rm -f ./coverage/unit*.cov
|
||||
# Run unit tests with coverage, for each package under 'internal'
|
||||
go list -f '{{.Name}}' ./internal/... | xargs -I {} go test -cover -covermode count -coverprofile ./coverage/unit-{}.cov ./internal/{}
|
||||
# ls -1 ./cmd | xargs -I {} go test -cover -covermode count -coverprofile ./coverage/unit-{}.cov ./cmd/{}/...
|
||||
echo 'mode: count' > ./coverage/unit.cov
|
||||
tail -q -n +2 ./coverage/unit-*.cov >> ./coverage/unit.cov
|
||||
go tool cover -html=./coverage/unit.cov -o ./coverage/unit.html
|
||||
|
||||
rm -f ./test/docker/coverage/*.cov
|
||||
rm -f ./coverage/docker.*
|
||||
mkdir -p ./test/docker/coverage/
|
||||
cd test/docker && TEST_IMAGE=$(MQ_IMAGE_ADVANCEDSERVER)-cover TEST_COVER=true go test $(TEST_OPTS_DOCKER)
|
||||
echo 'mode: count' > ./coverage/docker.cov
|
||||
tail -q -n +2 ./test/docker/coverage/*.cov >> ./coverage/docker.cov
|
||||
go tool cover -html=./coverage/docker.cov -o ./coverage/docker.html
|
||||
|
||||
echo 'mode: count' > ./coverage/combined.cov
|
||||
tail -q -n +2 ./coverage/unit.cov ./coverage/docker.cov >> ./coverage/combined.cov
|
||||
go tool cover -html=./coverage/combined.cov -o ./coverage/combined.html
|
||||
|
||||
define docker-build-mq
|
||||
# Create a temporary network to use for the build
|
||||
$(DOCKER) network create build
|
||||
# Start a web server to host the MQ downloadable (tar.gz) file
|
||||
$(DOCKER) run \
|
||||
--rm \
|
||||
--name $(BUILD_SERVER_CONTAINER) \
|
||||
--network build \
|
||||
--network-alias build \
|
||||
--volume $(DOWNLOADS_DIR):/usr/share/nginx/html:ro \
|
||||
--detach \
|
||||
nginx:alpine
|
||||
# Build the new image
|
||||
$(DOCKER) build \
|
||||
--tag $1 \
|
||||
--file $2 \
|
||||
--network build \
|
||||
--build-arg MQ_URL=http://build:80/$3 \
|
||||
--build-arg BASE_IMAGE=$(BASE_IMAGE) \
|
||||
--build-arg BUILDER_IMAGE=$(MQ_IMAGE_GOLANG_SDK) \
|
||||
--build-arg IMAGE_REVISION="$(IMAGE_REVISION)" \
|
||||
--build-arg IMAGE_SOURCE="$(IMAGE_SOURCE)" \
|
||||
--label IBM_PRODUCT_ID=$4 \
|
||||
--label IBM_PRODUCT_NAME=$5 \
|
||||
--label IBM_PRODUCT_VERSION=$6 \
|
||||
--build-arg MQ_PACKAGES="$(MQ_PACKAGES)" \
|
||||
. ; $(DOCKER) kill $(BUILD_SERVER_CONTAINER) && $(DOCKER) network rm build
|
||||
endef
|
||||
|
||||
DOCKER_SERVER_VERSION=$(shell docker version --format "{{ .Server.Version }}")
|
||||
DOCKER_CLIENT_VERSION=$(shell docker version --format "{{ .Client.Version }}")
|
||||
.PHONY: docker-version
|
||||
docker-version:
|
||||
@test "$(word 1,$(subst ., ,$(DOCKER_CLIENT_VERSION)))" -ge "17" || ("$(word 1,$(subst ., ,$(DOCKER_CLIENT_VERSION)))" -eq "17" && "$(word 2,$(subst ., ,$(DOCKER_CLIENT_VERSION)))" -ge "05") || (echo "Error: Docker client 17.05 or greater is required" && exit 1)
|
||||
@test "$(word 1,$(subst ., ,$(DOCKER_SERVER_VERSION)))" -ge "17" || ("$(word 1,$(subst ., ,$(DOCKER_SERVER_VERSION)))" -eq "17" && "$(word 2,$(subst ., ,$(DOCKER_CLIENT_VERSION)))" -ge "05") || (echo "Error: Docker server 17.05 or greater is required" && exit 1)
|
||||
|
||||
.PHONY: build-advancedserver
|
||||
build-advancedserver: MQ_SDK_ARCHIVE=$(MQ_ARCHIVE)
|
||||
build-advancedserver: downloads/$(MQ_ARCHIVE) docker-version build-golang-sdk-ex
|
||||
$(info $(SPACER)$(shell printf $(TITLE)"Build $(MQ_IMAGE_ADVANCEDSERVER)"$(END)))
|
||||
$(call docker-build-mq,$(MQ_IMAGE_ADVANCEDSERVER),Dockerfile-server,$(MQ_ARCHIVE),"4486e8c4cc9146fd9b3ce1f14a2dfc5b","IBM MQ Advanced",$(MQ_VERSION))
|
||||
|
||||
.PHONY: build-devserver
|
||||
# Target-specific variable to add web server into devserver image
|
||||
ifeq "$(findstring ubuntu,$(BASE_IMAGE))" "ubuntu"
|
||||
build-devserver: MQ_PACKAGES=ibmmq-server ibmmq-java ibmmq-jre ibmmq-gskit ibmmq-msg-.* ibmmq-samples ibmmq-ams ibmmq-web
|
||||
else
|
||||
build-devserver: MQ_PACKAGES=MQSeriesRuntime-*.rpm MQSeriesServer-*.rpm MQSeriesJava*.rpm MQSeriesJRE*.rpm MQSeriesGSKit*.rpm MQSeriesMsg*.rpm MQSeriesSamples*.rpm MQSeriesAMS-*.rpm MQSeriesWeb-*.rpm
|
||||
endif
|
||||
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 .
|
||||
|
||||
.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
|
||||
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))
|
||||
|
||||
.PHONY: build-sdk
|
||||
build-sdk: downloads/$(MQ_SDK_ARCHIVE) build-sdk-ex
|
||||
|
||||
.PHONY: build-sdk-ex
|
||||
ifeq "$(findstring ubuntu,$(BASE_IMAGE))" "ubuntu"
|
||||
build-sdk-ex: MQ_PACKAGES=ibmmq-sdk ibmmq-samples build-essential
|
||||
else
|
||||
build-sdk-ex: MQ_PACKAGES=MQSeriesRuntime-*.rpm MQSeriesSDK-*.rpm MQSeriesSamples*.rpm
|
||||
endif
|
||||
build-sdk-ex: docker-version docker-pull
|
||||
$(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
|
||||
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
|
||||
$(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))
|
||||
|
||||
.PHONY: docker-pull
|
||||
docker-pull:
|
||||
$(DOCKER) pull $(BASE_IMAGE)
|
||||
|
||||
include formatting.mk
|
||||
@@ -5,8 +5,6 @@
|
||||
**Note**: The `master` branch may be in an *unstable or even broken state* during development.
|
||||
To get a stable version, please use the correct [branch](https://github.com/ibm-messaging/mq-container/branches) for your MQ version, instead of the `master` branch.
|
||||
|
||||
<img src="https://raw.githubusercontent.com/IBM/charts/master/logo/ibm-mq-icon.svg?sanitize=true" width="100" alt="IBM MQ logo" />
|
||||
|
||||
## Overview
|
||||
|
||||
Run [IBM® MQ](http://www-03.ibm.com/software/products/en/ibm-mq) in a container.
|
||||
@@ -54,4 +52,4 @@ Note: The IBM MQ Advanced for Developers license does not permit further distrib
|
||||
|
||||
## Copyright
|
||||
|
||||
© Copyright IBM Corporation 2015, 2018
|
||||
© Copyright IBM Corporation 2015, 2019
|
||||
|
||||
23
buildah/Dockerfile
Normal file
23
buildah/Dockerfile
Normal file
@@ -0,0 +1,23 @@
|
||||
# © 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.
|
||||
|
||||
# Fedora includes more recent versions of buildah (need buildah V1.7 to get
|
||||
# multi-stage builds to work properly)
|
||||
FROM docker.io/fedora:29
|
||||
RUN yum install -y buildah
|
||||
COPY build.sh /usr/local/bin/build
|
||||
RUN chmod +x /usr/local/bin/build
|
||||
ENV STORAGE_DRIVER=vfs
|
||||
ENV BUILDAH_ISOLATION=chroot
|
||||
ENTRYPOINT ["build"]
|
||||
41
buildah/build.sh
Normal file
41
buildah/build.sh
Normal file
@@ -0,0 +1,41 @@
|
||||
#!/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.
|
||||
|
||||
# Build a container image from a Dockerfile using Buildah
|
||||
# If the Docker socket is available, the image will be pushed to Docker once built.
|
||||
|
||||
readonly IMAGE=$1:$2
|
||||
shift
|
||||
shift
|
||||
readonly SRC="/src"
|
||||
readonly OCI_DIR="/var/oci"
|
||||
|
||||
echo "****************************************"
|
||||
echo " Inside the Buildah container"
|
||||
echo "****************************************"
|
||||
set -ex
|
||||
# Build using the supplied options. Always pass the source directory in, and
|
||||
# use it as the build context
|
||||
buildah build-using-dockerfile --tag ${IMAGE} --volume /src:/src "$@" /src
|
||||
|
||||
if [ -e ${OCI_DIR} ]; then
|
||||
buildah push ${IMAGE} oci-archive:${OCI_DIR}/${IMAGE}
|
||||
fi
|
||||
|
||||
if [ -e /var/run/docker.sock ]; then
|
||||
buildah push ${IMAGE} docker-daemon:${IMAGE}
|
||||
fi
|
||||
51
buildah/mq-buildah
Executable file
51
buildah/mq-buildah
Executable file
@@ -0,0 +1,51 @@
|
||||
#!/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.
|
||||
|
||||
# Build and run a container image with Buildah installed
|
||||
|
||||
if [ $# -eq 0 ]; then
|
||||
echo "Usage: $0 imageName imageTag buildah-options"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
readonly SCRIPT_DIR="$( cd "$(dirname "$0")" ; pwd -P )"
|
||||
readonly DIR=${SCRIPT_DIR}/..
|
||||
readonly BUILDAH_IMAGE=mq-buildah
|
||||
RUN_OPTS="--volume ${DIR}:/src --cap-add SYS_ADMIN --rm --interactive --tty"
|
||||
CMD="docker"
|
||||
|
||||
# If Docker is installed, then map the Docker socket into the container, to
|
||||
# allow buildah to push the resulting image into Docker's image store
|
||||
# WARNING: This means that the buildah container can do anything the "docker"
|
||||
# can do.
|
||||
command -v docker
|
||||
if [[ $? == 0 && -e /var/run/docker.sock ]]; then
|
||||
RUN_OPTS="${RUN_OPTS} --volume /var/run/docker.sock:/var/run/docker.sock"
|
||||
fi
|
||||
|
||||
command -v podman
|
||||
if [[ $? -eq 0 ]]; then
|
||||
CMD="podman"
|
||||
OCI_DIR="/tmp/mq-buildah"
|
||||
mkdir -p ${OCI_DIR}
|
||||
RUN_OPTS="${RUN_OPTS} --volume ${OCI_DIR}:/var/oci"
|
||||
echo "Image archives will be written to ${OCI_DIR}"
|
||||
fi
|
||||
|
||||
${CMD} build --tag ${BUILDAH_IMAGE} --file ${SCRIPT_DIR}/Dockerfile ${SCRIPT_DIR}
|
||||
set -x
|
||||
${CMD} run ${RUN_OPTS} ${BUILDAH_IMAGE} "$@"
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2017
|
||||
© 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.
|
||||
@@ -41,7 +41,7 @@ func queueManagerHealthy() (bool, error) {
|
||||
return false, err
|
||||
}
|
||||
fmt.Printf("%s", out)
|
||||
if !strings.Contains(string(out), "(RUNNING)") {
|
||||
if !strings.Contains(string(out), "(RUNNING)") && !strings.Contains(string(out), "(RUNNING AS STANDBY)") && !strings.Contains(string(out), "(STARTING)") {
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
|
||||
@@ -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.
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"github.com/ibm-messaging/mq-container/internal/name"
|
||||
"github.com/ibm-messaging/mq-container/internal/ready"
|
||||
)
|
||||
|
||||
@@ -31,7 +32,14 @@ func main() {
|
||||
if !r || err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
name, err := name.GetQueueManagerName()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Check if the queue manager has a running listener
|
||||
if standby, _ := ready.IsRunningAsStandbyQM(name); !standby {
|
||||
conn, err := net.Dial("tcp", "127.0.0.1:1414")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
@@ -41,4 +49,8 @@ func main() {
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
} else {
|
||||
fmt.Printf("Detected queue manager running in standby mode")
|
||||
os.Exit(10)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
@@ -23,7 +23,9 @@ import (
|
||||
"syscall"
|
||||
|
||||
"github.com/ibm-messaging/mq-container/internal/command"
|
||||
"github.com/ibm-messaging/mq-container/internal/containerruntimelogger"
|
||||
"github.com/ibm-messaging/mq-container/internal/logger"
|
||||
"github.com/ibm-messaging/mq-container/internal/mqtemplate"
|
||||
"github.com/ibm-messaging/mq-container/internal/name"
|
||||
)
|
||||
|
||||
@@ -31,7 +33,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 +43,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
|
||||
@@ -89,20 +92,20 @@ func configureLogger() error {
|
||||
|
||||
func configureWeb(qmName string) error {
|
||||
out := "/etc/mqm/web/installations/Installation1/angular.persistence/admin.json"
|
||||
return processTemplateFile("/etc/mqm/admin.json.tpl", out, map[string]string{"QueueManagerName": qmName})
|
||||
return mqtemplate.ProcessTemplateFile("/etc/mqm/admin.json.tpl", out, map[string]string{"QueueManagerName": qmName}, log)
|
||||
}
|
||||
|
||||
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 +118,13 @@ func doMain() error {
|
||||
logTermination(err)
|
||||
return err
|
||||
}
|
||||
|
||||
err = containerruntimelogger.LogContainerDetails(log)
|
||||
if err != nil {
|
||||
logTermination(err)
|
||||
return err
|
||||
}
|
||||
|
||||
adminPassword, set := os.LookupEnv("MQ_ADMIN_PASSWORD")
|
||||
if set {
|
||||
err = setPassword("admin", adminPassword)
|
||||
@@ -143,14 +153,6 @@ func doMain() error {
|
||||
logTerminationf("Error getting queue manager name: %v", err)
|
||||
return err
|
||||
}
|
||||
ks, set := os.LookupEnv("MQ_TLS_KEYSTORE")
|
||||
if set {
|
||||
err = configureTLS(name, ks, os.Getenv("MQ_TLS_PASSPHRASE"))
|
||||
if err != nil {
|
||||
logTerminationf("Error configuring TLS: %v", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = configureWeb(name)
|
||||
if err != nil {
|
||||
@@ -170,7 +172,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)
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
@@ -17,6 +17,8 @@ package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/ibm-messaging/mq-container/internal/mqtemplate"
|
||||
)
|
||||
|
||||
func updateMQSC(appPasswordRequired bool) error {
|
||||
@@ -30,7 +32,7 @@ func updateMQSC(appPasswordRequired bool) error {
|
||||
if os.Getenv("MQ_DEV") == "true" {
|
||||
const mqscTemplate string = mqsc + ".tpl"
|
||||
// Re-configure channel if app password not set
|
||||
err := processTemplateFile(mqsc+".tpl", mqsc, map[string]string{"ChckClnt": checkClient})
|
||||
err := mqtemplate.ProcessTemplateFile(mqsc+".tpl", mqsc, map[string]string{"ChckClnt": checkClient}, log)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1,164 +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"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/ibm-messaging/mq-container/internal/command"
|
||||
)
|
||||
|
||||
func configureWebTLS(cms *KeyStore) error {
|
||||
dir := "/run/runmqdevserver/tls"
|
||||
ks := NewJKSKeyStore(filepath.Join(dir, "key.jks"), cms.Password)
|
||||
ts := NewJKSKeyStore(filepath.Join(dir, "trust.jks"), cms.Password)
|
||||
|
||||
log.Debug("Creating key store")
|
||||
err := ks.Create()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Debug("Creating trust store")
|
||||
err = ts.Create()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Debug("Importing keys")
|
||||
err = ks.Import(cms.Filename, cms.Password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
webConfigDir := "/etc/mqm/web/installations/Installation1/servers/mqweb"
|
||||
tlsConfig := filepath.Join(webConfigDir, "tls.xml")
|
||||
newTLSConfig := filepath.Join(webConfigDir, "tls-dev.xml")
|
||||
err = os.Remove(tlsConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// we symlink here to prevent issues on restart
|
||||
err = os.Symlink(newTLSConfig, tlsConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mqmUID, mqmGID, err := command.LookupMQM()
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
err = os.Chown(tlsConfig, mqmUID, mqmGID)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func configureTLS(qmName string, inputFile string, passPhrase string) error {
|
||||
log.Debug("Configuring TLS")
|
||||
|
||||
_, err := os.Stat(inputFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: Use a persisted file (on the volume) instead?
|
||||
dir := "/run/runmqdevserver/tls"
|
||||
keyFile := filepath.Join(dir, "key.kdb")
|
||||
|
||||
_, err = os.Stat(dir)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
// #nosec G301
|
||||
err = os.MkdirAll(dir, 0770)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mqmUID, mqmGID, err := command.LookupMQM()
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
err = os.Chown(dir, mqmUID, mqmGID)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
cms := NewCMSKeyStore(keyFile, passPhrase)
|
||||
|
||||
err = cms.Create()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = cms.CreateStash()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = cms.Import(inputFile, passPhrase)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
labels, err := cms.GetCertificateLabels()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(labels) == 0 {
|
||||
return fmt.Errorf("unable to find certificate label")
|
||||
}
|
||||
log.Debugf("Renaming certificate from %v", labels[0])
|
||||
const newLabel string = "devcert"
|
||||
err = cms.RenameCertificate(labels[0], newLabel)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var sslCipherSpec string
|
||||
if os.Getenv("MQ_DEV") == "true" {
|
||||
sslCipherSpec = "TLS_RSA_WITH_AES_128_CBC_SHA256"
|
||||
} else {
|
||||
sslCipherSpec = "' '"
|
||||
}
|
||||
|
||||
const mqsc string = "/etc/mqm/20-dev-tls.mqsc"
|
||||
const mqscTemplate string = mqsc + ".tpl"
|
||||
|
||||
err = processTemplateFile(mqscTemplate, mqsc, map[string]string{
|
||||
"SSLKeyR": filepath.Join(dir, "key"),
|
||||
"CertificateLabel": newLabel,
|
||||
"SSLCipherSpec": sslCipherSpec,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = configureWebTLS(cms)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -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.
|
||||
@@ -17,15 +17,13 @@ package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"syscall"
|
||||
|
||||
"github.com/ibm-messaging/mq-container/internal/command"
|
||||
)
|
||||
|
||||
func createVolume(path string) error {
|
||||
dataPath := filepath.Join(path, "data")
|
||||
func createVolume(dataPath string) error {
|
||||
fi, err := os.Stat(dataPath)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
|
||||
@@ -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.
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/ibm-messaging/mq-container/internal/command"
|
||||
@@ -36,15 +37,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)
|
||||
}
|
||||
@@ -66,7 +67,7 @@ func formatSimple(datetime string, message string) string {
|
||||
// mirrorSystemErrorLogs starts a goroutine to mirror the contents of the MQ system error logs
|
||||
func mirrorSystemErrorLogs(ctx context.Context, wg *sync.WaitGroup, mf mirrorFunc) (chan error, error) {
|
||||
// Always use the JSON log as the source
|
||||
return mirrorLog(ctx, wg, "/var/mqm/errors/AMQERR01.json", false, mf)
|
||||
return mirrorLog(ctx, wg, "/var/mqm/errors/AMQERR01.json", false, mf, false)
|
||||
}
|
||||
|
||||
// mirrorQueueManagerErrorLogs starts a goroutine to mirror the contents of the MQ queue manager error logs
|
||||
@@ -78,7 +79,7 @@ func mirrorQueueManagerErrorLogs(ctx context.Context, wg *sync.WaitGroup, name s
|
||||
return nil, err
|
||||
}
|
||||
f := filepath.Join(mqini.GetErrorLogDirectory(qm), "AMQERR01.json")
|
||||
return mirrorLog(ctx, wg, f, fromStart, mf)
|
||||
return mirrorLog(ctx, wg, f, fromStart, mf, true)
|
||||
}
|
||||
|
||||
func getDebug() bool {
|
||||
@@ -99,21 +100,35 @@ func configureLogger(name string) (mirrorFunc, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return log.LogDirect, nil
|
||||
return func(msg string, isQMLog bool) bool {
|
||||
obj, err := processLogMessage(msg)
|
||||
if err == nil && isQMLog && filterQMLogMessage(obj) {
|
||||
return false
|
||||
}
|
||||
if err != nil {
|
||||
log.Printf("Failed to unmarshall JSON - %v", msg)
|
||||
} else {
|
||||
fmt.Println(msg)
|
||||
}
|
||||
return true
|
||||
}, nil
|
||||
case "basic":
|
||||
log, err = logger.NewLogger(os.Stderr, d, false, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return func(msg string) {
|
||||
return func(msg string, isQMLog bool) bool {
|
||||
// Parse the JSON message, and print a simplified version
|
||||
var obj map[string]interface{}
|
||||
err := json.Unmarshal([]byte(msg), &obj)
|
||||
obj, err := processLogMessage(msg)
|
||||
if err == nil && isQMLog && filterQMLogMessage(obj) {
|
||||
return false
|
||||
}
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to Unmarshall JSON - %v", err)
|
||||
log.Printf("Failed to unmarshall JSON - %v", err)
|
||||
} else {
|
||||
fmt.Printf(formatSimple(obj["ibm_datetime"].(string), obj["message"].(string)))
|
||||
}
|
||||
return true
|
||||
}, nil
|
||||
default:
|
||||
log, err = logger.NewLogger(os.Stdout, d, false, name)
|
||||
@@ -124,6 +139,20 @@ func configureLogger(name string) (mirrorFunc, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func processLogMessage(msg string) (map[string]interface{}, error) {
|
||||
var obj map[string]interface{}
|
||||
err := json.Unmarshal([]byte(msg), &obj)
|
||||
return obj, err
|
||||
}
|
||||
|
||||
func filterQMLogMessage(obj map[string]interface{}) bool {
|
||||
hostname, err := os.Hostname()
|
||||
if os.Getenv("MQ_MULTI_INSTANCE") == "true" && err == nil && !strings.Contains(obj["host"].(string), hostname) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func logDiagnostics() {
|
||||
log.Debug("--- Start Diagnostics ---")
|
||||
|
||||
@@ -138,11 +167,20 @@ func logDiagnostics() {
|
||||
out, _, _ = command.Run("ls", "-l", "/mnt/mqm/data")
|
||||
log.Debugf("/mnt/mqm/data:\n%s", out)
|
||||
// #nosec G104
|
||||
out, _, _ = command.Run("ls", "-l", "/mnt/mqm-log/log")
|
||||
log.Debugf("/mnt/mqm-log/log:\n%s", out)
|
||||
// #nosec G104
|
||||
out, _, _ = command.Run("ls", "-l", "/mnt/mqm-data/qmgrs")
|
||||
log.Debugf("/mnt/mqm-data/qmgrs:\n%s", out)
|
||||
// #nosec G104
|
||||
out, _, _ = command.Run("ls", "-l", "/var/mqm")
|
||||
log.Debugf("/var/mqm:\n%s", out)
|
||||
// #nosec G104
|
||||
out, _, _ = command.Run("ls", "-l", "/var/mqm/errors")
|
||||
log.Debugf("/var/mqm/errors:\n%s", out)
|
||||
// #nosec G104
|
||||
out, _, _ = command.Run("ls", "-l", "/etc/mqm")
|
||||
log.Debugf("/etc/mqm:\n%s", out)
|
||||
|
||||
// Print out summary of any FDCs
|
||||
// #nosec G204
|
||||
|
||||
@@ -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,21 +20,47 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"flag"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/ibm-messaging/mq-container/internal/containerruntimelogger"
|
||||
"github.com/ibm-messaging/mq-container/internal/metrics"
|
||||
"github.com/ibm-messaging/mq-container/internal/name"
|
||||
"github.com/ibm-messaging/mq-container/internal/ready"
|
||||
"github.com/ibm-messaging/mq-container/internal/tls"
|
||||
)
|
||||
|
||||
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 = containerruntimelogger.LogContainerDetails(log)
|
||||
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,33 +87,80 @@ func doMain() error {
|
||||
// Enable diagnostic collecting on failure
|
||||
collectDiagOnFail = true
|
||||
|
||||
err = verifyCurrentUser()
|
||||
if *devFlag == false {
|
||||
err = containerruntimelogger.LogContainerDetails(log)
|
||||
if err != nil {
|
||||
logTermination(err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = createVolume("/mnt/mqm/data")
|
||||
if err != nil {
|
||||
logTermination(err)
|
||||
return err
|
||||
}
|
||||
err = createVolume("/mnt/mqm-log/log")
|
||||
if err != nil {
|
||||
logTermination(err)
|
||||
return err
|
||||
}
|
||||
err = createVolume("/mnt/mqm-data/qmgrs")
|
||||
if err != nil {
|
||||
logTermination(err)
|
||||
return err
|
||||
}
|
||||
|
||||
err = logConfig()
|
||||
if err != nil {
|
||||
logTermination(err)
|
||||
return err
|
||||
}
|
||||
|
||||
err = createVolume("/mnt/mqm")
|
||||
if err != nil {
|
||||
logTermination(err)
|
||||
return err
|
||||
}
|
||||
err = createDirStructure()
|
||||
if err != nil {
|
||||
logTermination(err)
|
||||
return err
|
||||
}
|
||||
|
||||
// handle /var/mqm/ permissions in upgrade to UBI
|
||||
if *initFlag {
|
||||
varMqmDirs := []string{
|
||||
"/var/mqm/config",
|
||||
"/var/mqm/conv",
|
||||
"/var/mqm/errors",
|
||||
"/var/mqm/exits",
|
||||
"/var/mqm/exits64",
|
||||
"/var/mqm/log",
|
||||
"/var/mqm/mqft",
|
||||
"/var/mqm/qmgrs",
|
||||
"/var/mqm/shared",
|
||||
"/var/mqm/sockets",
|
||||
"/var/mqm/trace",
|
||||
"/var/mqm/web",
|
||||
}
|
||||
err = configureOwnership(varMqmDirs)
|
||||
if err != nil {
|
||||
logTermination(err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// If init flag is set, exit now
|
||||
if *initFlag {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Print out versioning information
|
||||
logVersionInfo()
|
||||
|
||||
err = postInit(name)
|
||||
keylabel, cmsDB, p12Trust, _, err := tls.ConfigureTLSKeystores(keyDir, trustDir, keyStoreDir)
|
||||
if err != nil {
|
||||
logTermination(err)
|
||||
return err
|
||||
}
|
||||
|
||||
err = configureTLS(keylabel, cmsDB, *devFlag)
|
||||
if err != nil {
|
||||
logTermination(err)
|
||||
return err
|
||||
}
|
||||
|
||||
err = postInit(name, keylabel, p12Trust)
|
||||
if err != nil {
|
||||
logTermination(err)
|
||||
return err
|
||||
@@ -124,16 +197,18 @@ func doMain() error {
|
||||
logTermination(err)
|
||||
return err
|
||||
}
|
||||
err = startQueueManager()
|
||||
err = startQueueManager(name)
|
||||
if err != nil {
|
||||
logTermination(err)
|
||||
return err
|
||||
}
|
||||
if standby, _ := ready.IsRunningAsStandbyQM(name); !standby {
|
||||
err = configureQueueManager()
|
||||
if err != nil {
|
||||
logTermination(err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
enableMetrics := os.Getenv("MQ_ENABLE_METRICS")
|
||||
if enableMetrics == "true" || enableMetrics == "1" {
|
||||
|
||||
@@ -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.
|
||||
@@ -49,17 +49,18 @@ func waitForFile(ctx context.Context, path string) (os.FileInfo, error) {
|
||||
}
|
||||
}
|
||||
|
||||
type mirrorFunc func(msg string)
|
||||
type mirrorFunc func(msg string, isQMLog bool) bool
|
||||
|
||||
// mirrorAvailableMessages prints lines from the file, until no more are available
|
||||
func mirrorAvailableMessages(f *os.File, mf mirrorFunc) {
|
||||
func mirrorAvailableMessages(f *os.File, mf mirrorFunc, isQMLog bool) {
|
||||
scanner := bufio.NewScanner(f)
|
||||
count := 0
|
||||
for scanner.Scan() {
|
||||
t := scanner.Text()
|
||||
mf(t)
|
||||
if mf(t, isQMLog) {
|
||||
count++
|
||||
}
|
||||
}
|
||||
if count > 0 {
|
||||
log.Debugf("Mirrored %v log entries from %v", count, f.Name())
|
||||
}
|
||||
@@ -73,7 +74,7 @@ func mirrorAvailableMessages(f *os.File, mf mirrorFunc) {
|
||||
// mirrorLog tails the specified file, and logs each line to stdout.
|
||||
// This is useful for usability, as the container console log can show
|
||||
// messages from the MQ error logs.
|
||||
func mirrorLog(ctx context.Context, wg *sync.WaitGroup, path string, fromStart bool, mf mirrorFunc) (chan error, error) {
|
||||
func mirrorLog(ctx context.Context, wg *sync.WaitGroup, path string, fromStart bool, mf mirrorFunc, isQMLog bool) (chan error, error) {
|
||||
errorChannel := make(chan error, 1)
|
||||
var offset int64 = -1
|
||||
var f *os.File
|
||||
@@ -147,7 +148,7 @@ func mirrorLog(ctx context.Context, wg *sync.WaitGroup, path string, fromStart b
|
||||
closing := false
|
||||
for {
|
||||
// If there's already data there, mirror it now.
|
||||
mirrorAvailableMessages(f, mf)
|
||||
mirrorAvailableMessages(f, mf, isQMLog)
|
||||
// Wait for the new log file (after rotation)
|
||||
newFI, err := waitForFile(ctx, path)
|
||||
if err != nil {
|
||||
@@ -161,7 +162,7 @@ func mirrorLog(ctx context.Context, wg *sync.WaitGroup, path string, fromStart b
|
||||
// log rotation happens before we can open the new file, then we
|
||||
// could skip all those messages. This could happen with a very small
|
||||
// MQ error log size.
|
||||
mirrorAvailableMessages(f, mf)
|
||||
mirrorAvailableMessages(f, mf, isQMLog)
|
||||
err = f.Close()
|
||||
if err != nil {
|
||||
log.Errorf("Unable to close mirror file handle: %v", err)
|
||||
@@ -176,7 +177,7 @@ func mirrorLog(ctx context.Context, wg *sync.WaitGroup, path string, fromStart b
|
||||
}
|
||||
fi = newFI
|
||||
// Don't seek this time, because we know it's a new file
|
||||
mirrorAvailableMessages(f, mf)
|
||||
mirrorAvailableMessages(f, mf, isQMLog)
|
||||
}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
|
||||
@@ -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.
|
||||
@@ -41,9 +41,10 @@ func TestMirrorLogWithoutRotation(t *testing.T) {
|
||||
count := 0
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
var wg sync.WaitGroup
|
||||
_, err = mirrorLog(ctx, &wg, tmp.Name(), true, func(msg string) {
|
||||
_, err = mirrorLog(ctx, &wg, tmp.Name(), true, func(msg string, isQMLog bool) bool {
|
||||
count++
|
||||
})
|
||||
return true
|
||||
}, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -82,9 +83,10 @@ func TestMirrorLogWithRotation(t *testing.T) {
|
||||
count := 0
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
var wg sync.WaitGroup
|
||||
_, err = mirrorLog(ctx, &wg, tmp.Name(), true, func(msg string) {
|
||||
_, err = mirrorLog(ctx, &wg, tmp.Name(), true, func(msg string, isQMLog bool) bool {
|
||||
count++
|
||||
})
|
||||
return true
|
||||
}, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -135,9 +137,10 @@ func testMirrorLogExistingFile(t *testing.T, newQM bool) int {
|
||||
count := 0
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
var wg sync.WaitGroup
|
||||
_, err = mirrorLog(ctx, &wg, tmp.Name(), newQM, func(msg string) {
|
||||
_, err = mirrorLog(ctx, &wg, tmp.Name(), newQM, func(msg string, isQMLog bool) bool {
|
||||
count++
|
||||
})
|
||||
return true
|
||||
}, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -179,8 +182,9 @@ func TestMirrorLogCancelWhileWaiting(t *testing.T) {
|
||||
cancel()
|
||||
wg.Wait()
|
||||
}()
|
||||
_, err := mirrorLog(ctx, &wg, "fake.log", true, func(msg string) {
|
||||
})
|
||||
_, err := mirrorLog(ctx, &wg, "fake.log", true, func(msg string, isQMLog bool) bool {
|
||||
return true
|
||||
}, false)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
// +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.
|
||||
@@ -19,23 +17,26 @@ package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/ibm-messaging/mq-container/internal/tls"
|
||||
)
|
||||
|
||||
// postInit is run after /var/mqm is set up
|
||||
// This version of postInit is only included as part of the MQ Advanced for Developers build
|
||||
func postInit(name string) error {
|
||||
disable := os.Getenv("MQ_DISABLE_WEB_CONSOLE")
|
||||
if disable != "true" && disable != "1" {
|
||||
// Configure the web server (if installed)
|
||||
err := configureWebServer()
|
||||
func postInit(name, keylabel string, p12Trust tls.KeyStoreData) error {
|
||||
enableWebServer := os.Getenv("MQ_ENABLE_EMBEDDED_WEB_SERVER")
|
||||
if enableWebServer == "true" || enableWebServer == "1" {
|
||||
// Configure the web server (if enabled)
|
||||
keystore, err := configureWebServer(keylabel, p12Trust)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Start the web server, in the background (if installed)
|
||||
// WARNING: No error handling or health checking available for the web server,
|
||||
// which is why it's limited to use with MQ Advanced for Developers only
|
||||
// WARNING: No error handling or health checking available for the web server
|
||||
go func() {
|
||||
startWebServer()
|
||||
err = startWebServer(keystore, p12Trust.Password)
|
||||
if err != nil {
|
||||
log.Printf("Error starting web server: %v", err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
return nil
|
||||
64
cmd/runmqserver/process.go
Normal file
64
cmd/runmqserver/process.go
Normal 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
|
||||
}
|
||||
@@ -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,14 +16,20 @@ limitations under the License.
|
||||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/ibm-messaging/mq-container/internal/command"
|
||||
containerruntime "github.com/ibm-messaging/mq-container/internal/containerruntime"
|
||||
"github.com/ibm-messaging/mq-container/internal/mqscredact"
|
||||
"github.com/ibm-messaging/mq-container/internal/ready"
|
||||
)
|
||||
|
||||
// createDirStructure creates the default MQ directory structure under /var/mqm
|
||||
@@ -37,21 +43,90 @@ func createDirStructure() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// configureOwnership recursively handles ownership of files within the given filepath
|
||||
func configureOwnership(paths []string) error {
|
||||
uid, gid, err := command.LookupMQM()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var fileInfo *unix.Stat_t
|
||||
fileInfo = new(unix.Stat_t)
|
||||
for _, root := range paths {
|
||||
_, err = os.Stat(root)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
err = filepath.Walk(root, func(from string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
to := fmt.Sprintf("%v%v", root, from[len(root):])
|
||||
err = unix.Stat(to, fileInfo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fileUID := fmt.Sprint(fileInfo.Uid)
|
||||
if strings.Compare(fileUID, "999") == 0 {
|
||||
err = os.Chown(to, uid, gid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// createQueueManager creates a queue manager, if it doesn't already exist.
|
||||
// It returns true if one was created, or false if one already existed
|
||||
// It returns true if one was created (or a standby was created), or false if one already existed
|
||||
func createQueueManager(name string) (bool, error) {
|
||||
log.Printf("Creating queue manager %v", name)
|
||||
out, rc, err := command.Run("crtmqm", "-q", "-p", "1414", name)
|
||||
if err != nil {
|
||||
// 8=Queue manager exists, which is fine
|
||||
if rc == 8 {
|
||||
|
||||
// Run 'dspmqinf' to check if 'mqs.ini' configuration file exists
|
||||
// If command succeeds, the queue manager (or standby queue manager) has already been created
|
||||
_, _, err := command.Run("dspmqinf", name)
|
||||
if err == nil {
|
||||
log.Printf("Detected existing queue manager %v", name)
|
||||
return false, nil
|
||||
}
|
||||
log.Printf("crtmqm returned %v", rc)
|
||||
log.Println(string(out))
|
||||
|
||||
mounts, err := containerruntime.GetMounts()
|
||||
if err != nil {
|
||||
log.Printf("Error getting mounts for queue manager")
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Check if 'qm.ini' configuration file exists for the queue manager
|
||||
// TODO : handle possible race condition - use a file lock?
|
||||
dataDir := getQueueManagerDataDir(mounts, name)
|
||||
_, err = os.Stat(filepath.Join(dataDir, "qm.ini"))
|
||||
if err != nil {
|
||||
// If 'qm.ini' is not found - run 'crtmqm' to create a new queue manager
|
||||
args := getCreateQueueManagerArgs(mounts, name)
|
||||
out, rc, err := command.Run("crtmqm", args...)
|
||||
if err != nil {
|
||||
log.Printf("Error %v creating queue manager: %v", rc, string(out))
|
||||
return false, err
|
||||
}
|
||||
} else {
|
||||
// If 'qm.ini' is found - run 'addmqinf' to create a standby queue manager with existing configuration
|
||||
args := getCreateStandbyQueueManagerArgs(name)
|
||||
out, rc, err := command.Run("addmqinf", args...)
|
||||
if err != nil {
|
||||
log.Printf("Error %v creating standby queue manager: %v", rc, string(out))
|
||||
return false, err
|
||||
}
|
||||
log.Println("Created standby queue manager")
|
||||
return true, nil
|
||||
}
|
||||
log.Println("Created queue manager")
|
||||
return true, nil
|
||||
}
|
||||
|
||||
@@ -68,10 +143,15 @@ func updateCommandLevel() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func startQueueManager() error {
|
||||
func startQueueManager(name string) error {
|
||||
log.Println("Starting queue manager")
|
||||
out, rc, err := command.Run("strmqm")
|
||||
out, rc, err := command.Run("strmqm", "-x", name)
|
||||
if err != nil {
|
||||
// 30=standby queue manager started, which is fine
|
||||
if rc == 30 {
|
||||
log.Printf("Started standby queue manager")
|
||||
return nil
|
||||
}
|
||||
log.Printf("Error %v starting queue manager: %v", rc, string(out))
|
||||
return err
|
||||
}
|
||||
@@ -86,43 +166,49 @@ 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
|
||||
verify := exec.Command("runmqsc", "-v", "-e")
|
||||
// #nosec G204 - command is fixed, no injection vector
|
||||
cmd := exec.Command("runmqsc")
|
||||
stdin, err := cmd.StdinPipe()
|
||||
// Read mqsc file into variable
|
||||
// #nosec G304 - filename variable is derived from contents of 'configDir' which is a defined constant
|
||||
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)
|
||||
verifyBuffer := buffer
|
||||
|
||||
// Buffer mqsc to stdin of runmqsc
|
||||
cmd.Stdin = &buffer
|
||||
verify.Stdin = &verifyBuffer
|
||||
|
||||
// Verify the MQSC commands
|
||||
out, err := verify.CombinedOutput()
|
||||
if err != nil {
|
||||
log.Errorf("Error reading %v: %v", abs, err)
|
||||
log.Errorf("Error verifying MQSC file %v (%v):\n\t%v", file.Name(), err, formatMQSCOutput(string(out)))
|
||||
return fmt.Errorf("Error verifying MQSC file %v (%v):\n\t%v", file.Name(), err, formatMQSCOutput(string(out)))
|
||||
}
|
||||
err = f.Close()
|
||||
|
||||
// Run runmqsc command
|
||||
out, err = cmd.CombinedOutput()
|
||||
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
|
||||
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,11 +216,77 @@ func configureQueueManager() error {
|
||||
|
||||
func stopQueueManager(name string) error {
|
||||
log.Println("Stopping queue manager")
|
||||
out, _, err := command.Run("endmqm", "-w", name)
|
||||
isStandby, err := ready.IsRunningAsStandbyQM(name)
|
||||
if err != nil {
|
||||
log.Printf("Error stopping queue manager: %v", string(out))
|
||||
log.Printf("Error getting status for queue manager %v: ", name, err.Error())
|
||||
return err
|
||||
}
|
||||
args := []string{"-w", "-r", name}
|
||||
if os.Getenv("MQ_MULTI_INSTANCE") == "true" {
|
||||
if isStandby {
|
||||
args = []string{"-x", name}
|
||||
} else {
|
||||
args = []string{"-s", "-w", "-r", name}
|
||||
}
|
||||
}
|
||||
out, rc, err := command.Run("endmqm", args...)
|
||||
if err != nil {
|
||||
log.Printf("Error %v stopping queue manager: %v", rc, string(out))
|
||||
return err
|
||||
}
|
||||
if isStandby {
|
||||
log.Printf("Stopped standby queue manager")
|
||||
} else {
|
||||
log.Println("Stopped queue manager")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func formatMQSCOutput(out string) string {
|
||||
// redact sensitive information
|
||||
out, _ = mqscredact.Redact(out)
|
||||
|
||||
// add tab characters to make it more readable as part of the log
|
||||
return strings.Replace(string(out), "\n", "\n\t", -1)
|
||||
}
|
||||
|
||||
func isStandbyQueueManager(name string) (bool, error) {
|
||||
out, rc, err := command.Run("dspmq", "-n", "-m", name)
|
||||
if err != nil {
|
||||
log.Printf("Error %v getting status for queue manager %v: %v", rc, name, string(out))
|
||||
return false, err
|
||||
}
|
||||
if strings.Contains(string(out), "(RUNNING AS STANDBY)") {
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func getQueueManagerDataDir(mounts map[string]string, name string) string {
|
||||
dataDir := filepath.Join("/var/mqm/qmgrs", name)
|
||||
if _, ok := mounts["/mnt/mqm-data"]; ok {
|
||||
dataDir = filepath.Join("/mnt/mqm-data/qmgrs", name)
|
||||
}
|
||||
return dataDir
|
||||
}
|
||||
|
||||
func getCreateQueueManagerArgs(mounts map[string]string, name string) []string {
|
||||
args := []string{"-q", "-p", "1414"}
|
||||
if _, ok := mounts["/mnt/mqm-log"]; ok {
|
||||
args = append(args, "-ld", "/mnt/mqm-log/log")
|
||||
}
|
||||
if _, ok := mounts["/mnt/mqm-data"]; ok {
|
||||
args = append(args, "-md", "/mnt/mqm-data/qmgrs")
|
||||
}
|
||||
args = append(args, name)
|
||||
return args
|
||||
}
|
||||
|
||||
func getCreateStandbyQueueManagerArgs(name string) []string {
|
||||
args := []string{"-s", "QueueManager"}
|
||||
args = append(args, "-v", fmt.Sprintf("Name=%v", name))
|
||||
args = append(args, "-v", fmt.Sprintf("Directory=%v", name))
|
||||
args = append(args, "-v", "Prefix=/var/mqm")
|
||||
args = append(args, "-v", fmt.Sprintf("DataPath=/mnt/mqm-data/qmgrs/%v", name))
|
||||
return args
|
||||
}
|
||||
|
||||
143
cmd/runmqserver/tls.go
Normal file
143
cmd/runmqserver/tls.go
Normal file
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
© 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 main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/ibm-messaging/mq-container/internal/command"
|
||||
"github.com/ibm-messaging/mq-container/internal/mqtemplate"
|
||||
"github.com/ibm-messaging/mq-container/internal/tls"
|
||||
)
|
||||
|
||||
// Location to store the keystores
|
||||
const keyStoreDir = "/run/runmqserver/tls/"
|
||||
|
||||
// KeyDir is the location of the certificate keys to import
|
||||
const keyDir = "/etc/mqm/pki/keys"
|
||||
|
||||
// TrustDir is the location of the Certifates to add
|
||||
const trustDir = "/etc/mqm/pki/trust"
|
||||
|
||||
// configureWebTLS configures TLS for Web Console
|
||||
func configureWebTLS(label string) error {
|
||||
// Return immediately if we have no certificate to use as identity
|
||||
if label == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
webConfigDir := "/etc/mqm/web/installations/Installation1/servers/mqweb"
|
||||
tls := "tls.xml"
|
||||
|
||||
tlsConfig := filepath.Join(webConfigDir, tls)
|
||||
newTLSConfig := filepath.Join(webConfigDir, tls+".tpl")
|
||||
err := os.Remove(tlsConfig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Could not delete file %s: %v", tlsConfig, err)
|
||||
}
|
||||
// we symlink here to prevent issues on restart
|
||||
err = os.Symlink(newTLSConfig, tlsConfig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Could not create symlink %s->%s: %v", newTLSConfig, tlsConfig, err)
|
||||
}
|
||||
mqmUID, mqmGID, err := command.LookupMQM()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Could not find mqm user or group: %v", err)
|
||||
}
|
||||
err = os.Chown(tlsConfig, mqmUID, mqmGID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Could change ownership of %s to mqm: %v", tlsConfig, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// configureTLSDev configures TLS for developer defaults
|
||||
func configureTLSDev() error {
|
||||
const mqsc string = "/etc/mqm/20-dev-tls.mqsc"
|
||||
const mqscTemplate string = mqsc + ".tpl"
|
||||
const sslCipherSpec string = "TLS_RSA_WITH_AES_128_CBC_SHA256"
|
||||
|
||||
if os.Getenv("MQ_DEV") == "true" {
|
||||
err := mqtemplate.ProcessTemplateFile(mqscTemplate, mqsc, map[string]string{
|
||||
"SSLCipherSpec": sslCipherSpec,
|
||||
}, log)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
_, err := os.Stat(mqsc)
|
||||
if !os.IsNotExist(err) {
|
||||
err = os.Remove(mqsc)
|
||||
if err != nil {
|
||||
log.Errorf("Error removing file %s: %v", mqsc, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// configureTLS configures TLS for queue manager
|
||||
func configureTLS(certLabel string, cmsKeystore tls.KeyStoreData, devmode bool) error {
|
||||
log.Debug("Configuring TLS")
|
||||
|
||||
const mqsc string = "/etc/mqm/15-tls.mqsc"
|
||||
const mqscTemplate string = mqsc + ".tpl"
|
||||
|
||||
err := mqtemplate.ProcessTemplateFile(mqscTemplate, mqsc, map[string]string{
|
||||
"SSLKeyR": strings.TrimSuffix(cmsKeystore.Keystore.Filename, ".kdb"),
|
||||
"CertificateLabel": certLabel,
|
||||
}, log)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if devmode && certLabel != "" {
|
||||
err = configureTLSDev()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// configureSSOTLS configures MQ Console TLS for Single Sign-On
|
||||
func configureSSOTLS(p12TrustStore tls.KeyStoreData) (string, error) {
|
||||
// TODO find way to supply this
|
||||
// Override the webstore variables to hard coded defaults
|
||||
webKeyStoreName := tls.IntegrationDefaultLabel + ".p12"
|
||||
|
||||
// Check keystore exists
|
||||
ks := filepath.Join(keyStoreDir, webKeyStoreName)
|
||||
_, err := os.Stat(ks)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Failed to find existing keystore %s: %v", ks, err)
|
||||
}
|
||||
|
||||
// Check truststore exists
|
||||
_, err = os.Stat(p12TrustStore.Keystore.Filename)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Failed to find existing truststore %s: %v", p12TrustStore.Keystore.Filename, err)
|
||||
}
|
||||
|
||||
return webKeyStoreName, nil
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
// +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.
|
||||
@@ -19,35 +17,61 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/ibm-messaging/mq-container/internal/command"
|
||||
"github.com/ibm-messaging/mq-container/internal/copy"
|
||||
"github.com/ibm-messaging/mq-container/internal/mqtemplate"
|
||||
"github.com/ibm-messaging/mq-container/internal/tls"
|
||||
)
|
||||
|
||||
func startWebServer() error {
|
||||
func startWebServer(keystore, keystorepw string) error {
|
||||
_, err := os.Stat("/opt/mqm/bin/strmqweb")
|
||||
if err != nil && os.IsNotExist(err) {
|
||||
log.Debug("Skipping web server, because it's not installed")
|
||||
return nil
|
||||
}
|
||||
log.Println("Starting web server")
|
||||
// #nosec G204 - command is fixed, no injection vector
|
||||
cmd := exec.Command("strmqweb")
|
||||
// Set a default app password for the web server, if one isn't already set
|
||||
_, set := os.LookupEnv("MQ_APP_PASSWORD")
|
||||
if !set {
|
||||
// Take all current environment variables, and add the app password
|
||||
cmd.Env = append(os.Environ(), "MQ_APP_PASSWORD=passw0rd")
|
||||
} else {
|
||||
cmd.Env = os.Environ()
|
||||
}
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{}
|
||||
|
||||
// TLS enabled
|
||||
if keystore != "" {
|
||||
cmd.Env = append(cmd.Env, "AMQ_WEBKEYSTORE="+keystore)
|
||||
cmd.Env = append(cmd.Env, "AMQ_WEBKEYSTOREPW="+keystorepw)
|
||||
}
|
||||
|
||||
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))
|
||||
@@ -57,45 +81,83 @@ func startWebServer() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// CopyFile copies the specified file
|
||||
func CopyFile(src, dest string) error {
|
||||
log.Debugf("Copying file %v to %v", src, dest)
|
||||
in, err := os.Open(src)
|
||||
if err != nil {
|
||||
return err
|
||||
func configureSSO(p12TrustStore tls.KeyStoreData) (string, error) {
|
||||
// Ensure all required environment variables are set for SSO
|
||||
requiredEnvVars := []string{
|
||||
"MQ_WEB_ADMIN_USERS",
|
||||
"MQ_OIDC_CLIENT_ID",
|
||||
"MQ_OIDC_CLIENT_SECRET",
|
||||
"MQ_OIDC_UNIQUE_USER_IDENTIFIER",
|
||||
"MQ_OIDC_AUTHORIZATION_ENDPOINT",
|
||||
"MQ_OIDC_TOKEN_ENDPOINT",
|
||||
"MQ_OIDC_JWK_ENDPOINT",
|
||||
"MQ_OIDC_ISSUER_IDENTIFIER",
|
||||
"MQ_OIDC_CERTIFICATE",
|
||||
}
|
||||
defer in.Close()
|
||||
|
||||
out, err := os.OpenFile(dest, os.O_CREATE|os.O_WRONLY, 0770)
|
||||
defer out.Close()
|
||||
|
||||
_, err = io.Copy(out, in)
|
||||
if err != nil {
|
||||
return err
|
||||
for _, envVar := range requiredEnvVars {
|
||||
if len(os.Getenv(envVar)) == 0 {
|
||||
return "", fmt.Errorf("%v must be set when MQ_BETA_ENABLE_SSO=true", envVar)
|
||||
}
|
||||
err = out.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
func configureWebServer() error {
|
||||
_, err := os.Stat("/opt/mqm/bin/strmqweb")
|
||||
// Check mqweb directory exists
|
||||
const mqwebDir string = "/etc/mqm/web/installations/Installation1/servers/mqweb"
|
||||
_, err := os.Stat(mqwebDir)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return nil
|
||||
return "", nil
|
||||
}
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Process SSO template for generating file mqwebuser.xml
|
||||
adminUsers := strings.Split(os.Getenv("MQ_WEB_ADMIN_USERS"), "\n")
|
||||
err = mqtemplate.ProcessTemplateFile(mqwebDir+"/mqwebuser.xml.tpl", mqwebDir+"/mqwebuser.xml", map[string][]string{"AdminUser": adminUsers}, log)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Configure SSO TLS
|
||||
return configureSSOTLS(p12TrustStore)
|
||||
}
|
||||
|
||||
func configureWebServer(keyLabel string, p12Trust tls.KeyStoreData) (string, error) {
|
||||
var keystore string
|
||||
// Configure TLS for Web Console first if we have a certificate to use
|
||||
err := configureWebTLS(keyLabel)
|
||||
if err != nil {
|
||||
return keystore, err
|
||||
}
|
||||
if keyLabel != "" {
|
||||
keystore = keyLabel + ".p12"
|
||||
}
|
||||
|
||||
// Configure Single-Sign-On for the web server (if enabled)
|
||||
enableSSO := os.Getenv("MQ_BETA_ENABLE_SSO")
|
||||
if enableSSO == "true" || enableSSO == "1" {
|
||||
keystore, err = configureSSO(p12Trust)
|
||||
if err != nil {
|
||||
return keystore, err
|
||||
}
|
||||
}
|
||||
_, err = os.Stat("/opt/mqm/bin/strmqweb")
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return keystore, nil
|
||||
}
|
||||
return keystore, err
|
||||
}
|
||||
const webConfigDir string = "/etc/mqm/web"
|
||||
_, err = os.Stat(webConfigDir)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return nil
|
||||
return keystore, nil
|
||||
}
|
||||
return err
|
||||
return keystore, err
|
||||
}
|
||||
uid, gid, err := command.LookupMQM()
|
||||
if err != nil {
|
||||
return err
|
||||
return keystore, err
|
||||
}
|
||||
const prefix string = "/etc/mqm/web"
|
||||
err = filepath.Walk(prefix, func(from string, info os.FileInfo, err error) error {
|
||||
@@ -114,6 +176,7 @@ func configureWebServer() error {
|
||||
}
|
||||
if info.IsDir() {
|
||||
if !exists {
|
||||
// #nosec G301 - write group permissions are required
|
||||
err := os.MkdirAll(to, 0770)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -126,7 +189,7 @@ func configureWebServer() error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
err := CopyFile(from, to)
|
||||
err := copy.CopyFile(from, to)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return err
|
||||
@@ -138,5 +201,5 @@ func configureWebServer() error {
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return err
|
||||
return keystore, err
|
||||
}
|
||||
|
||||
@@ -2,40 +2,22 @@
|
||||
|
||||
## Prerequisites
|
||||
|
||||
### Prerequisites for building an Ubuntu image
|
||||
If you want to build a container image with Ubuntu Linux as the base OS, then you need to have the following tools installed:
|
||||
You need to have the following tools installed:
|
||||
|
||||
* [Docker](https://www.docker.com/) V17.06.1 or later
|
||||
* [GNU make](https://www.gnu.org/software/make/)
|
||||
|
||||
If you are working in the Windows Subsystem for Linux, follow [this guide by Microsoft to set up Docker](https://blogs.msdn.microsoft.com/commandline/2017/12/08/cross-post-wsl-interoperability-with-docker/) first.
|
||||
|
||||
### Prerequisites for building a Red Hat Enterprise Linux image
|
||||
If you want to build 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:
|
||||
|
||||
* [`buildah`](https://buildah.io) (available in `rhel-7-server-extras`)
|
||||
* [`podman`](https://podman.io) (available in `rhel-7-server-extras`)
|
||||
|
||||
In addition, you need the following commonly installed tools:
|
||||
|
||||
* `bash`
|
||||
* `coreutils`
|
||||
* `findutils`
|
||||
* `make`
|
||||
* `sed`
|
||||
* `shadow-utils`
|
||||
* `tar`
|
||||
|
||||
## Building a production image
|
||||
This procedure works for building the MQ Continuous Delivery release, on `x86_64`, `ppc64le` and `s390x` architectures.
|
||||
|
||||
This procedure works for building the MQ Continuous Delivery release, on `amd64`, `ppc64le` and `s390x` architectures.
|
||||
|
||||
1. Create a `downloads` directory in the root of this repository
|
||||
2. Download MQ from [IBM Passport Advantage](https://www.ibm.com/software/passportadvantage/) or [IBM Fix Central](https://www.ibm.com/support/fixcentral), and place the downloaded file (for example, `IBM_MQ_9.1.1_UBUNTU_X86-64.tar.gz` for MQ V9.1.1 for Ubuntu on x86_64 architecture) in the `downloads` directory
|
||||
2. Download MQ from [IBM Passport Advantage](https://www.ibm.com/software/passportadvantage/) or [IBM Fix Central](https://www.ibm.com/support/fixcentral), and place the downloaded file (for example, `IBM_MQ_9.1.2_UBUNTU_X86-64.tar.gz`) in the `downloads` directory
|
||||
3. Run `make build-advancedserver`
|
||||
|
||||
> **Warning**: Note that MQ offers two different sets of packaging on Linux: one is called "MQ for Linux" and contains RPM files for installing on Red Hat Enterprise Linux and SUSE Linux Enterprise Server. The other package is called "MQ for Ubuntu", and contains DEB files for installing on Ubuntu.
|
||||
|
||||
On a Red Hat Enterprise Linux host, the command `make build-advancedserver` will build a container image using Red Hat Enterprise Linux as the base. On all other hosts, the base image will be Ubuntu.
|
||||
> **Warning**: Note that MQ offers two different sets of packaging on Linux: one is called "MQ for Linux" and contains RPM files for installing on Red Hat Enterprise Linux and SUSE Linux Enterprise Server. The MQ container build uses a Red Hat Universal Base Image, so you need the "MQ for Linux" RPM files.
|
||||
|
||||
You can build a different version of MQ by setting the `MQ_VERSION` environment variable, for example:
|
||||
|
||||
@@ -50,10 +32,15 @@ MQ_ARCHIVE=mq-1.2.3.4.tar.gz MQ_VERSION=1.2.3.4 make build-advancedserver
|
||||
```
|
||||
|
||||
## Building a developer image
|
||||
Run `make build-devserver`, which will download the latest version of MQ Advanced for Developers from IBM developerWorks. This is currently only available on the `x86_64` architecture. On a Red Hat Enterprise Linux host, this command will build a container image using Red Hat Enterprise Linux as the base. On all other hosts, the base image will be Ubuntu.
|
||||
Run `make build-devserver`, which will download the latest version of MQ Advanced for Developers from IBM developerWorks. This is currently only available on the `amd64` architecture.
|
||||
|
||||
You can use the environment variable `MQ_ARCHIVE_DEV` to specify an alternative local file to install from (which must be in the `downloads` directory).
|
||||
|
||||
## Building from a Red Hat Enterprise Linux host
|
||||
Red Hat Enterprise Linux (RHEL) offers a suite of container tools, including Buildah for building container images, and Podman for running containers. Buildah can accept input described in a [Dockerfile](https://docs.docker.com/engine/reference/builder/). This MQ sample uses a multi-stage build, which requires a recent version of Podman, which is not yet available in Red Hat Enterprise Linux V7. Therefore, if you are on a RHEL host, then the `build-devserver` and `build-advancedserver` targets are run using a more recent version of Buildah from inside a container.
|
||||
|
||||
The containerized build process on a RHEL host will write an OCI compliant archive file to `/tmp/mq-buildah`. If a version of Docker is installed on the host, it will also push the image into Docker's internal image registry.
|
||||
|
||||
## Installed components
|
||||
|
||||
This image includes the core MQ server, Java, language packs, and GSKit. This can be configured by setting the `MQ_PACKAGES` argument to `make`. For the Ubuntu-based image, you can also directly set a [Docker build argument](https://docs.docker.com/engine/reference/commandline/build/#set-build-time-variables-build-arg).
|
||||
This image includes the core MQ server, Java, language packs, GSKit, and web server. This can be configured by setting the `MQ_PACKAGES` argument to `make`.
|
||||
|
||||
@@ -9,8 +9,8 @@ The MQ Developer Defaults supports some customization options, these are all con
|
||||
* **MQ_DEV** - Set this to `false` to stop the default objects being created.
|
||||
* **MQ_ADMIN_PASSWORD** - Changes the password of the `admin` user. Must be at least 8 characters long.
|
||||
* **MQ_APP_PASSWORD** - Changes the password of the app user. If set, this will cause the `DEV.APP.SVRCONN` channel to become secured and only allow connections that supply a valid userid and password. Must be at least 8 characters long.
|
||||
* **MQ_TLS_KEYSTORE** - Allows you to supply the location of a PKCS#12 keystore containing a single certificate which you want to use in both the web console and the queue manager. Requires `MQ_TLS_PASSPHRASE`. When enabled the channels created will be secured using the `TLS_RSA_WITH_AES_128_CBC_SHA256` CipherSpec. *Note*: you will need to make the keystore available inside your container, this can be done by mounting a volume to your container.
|
||||
* **MQ_TLS_PASSPHRASE** - Passphrase for the keystore referenced in `MQ_TLS_KEYSTORE`.
|
||||
* **MQ_TLS_KEYSTORE** - **DEPRECATED**. See section `Supplying TLS certificates` in [usage document](usage.md). Allows you to supply the location of a PKCS#12 keystore containing a single certificate which you want to use in both the web console and the queue manager. Requires `MQ_TLS_PASSPHRASE`. When enabled the channels created will be secured using the `TLS_RSA_WITH_AES_128_CBC_SHA256` CipherSpec. *Note*: you will need to make the keystore available inside your container, this can be done by mounting a volume to your container.
|
||||
* **MQ_TLS_PASSPHRASE** - **DEPRECATED**. See section `Supplying TLS certificates` in [usage document](usage.md). Passphrase for the keystore referenced in `MQ_TLS_KEYSTORE`.
|
||||
|
||||
## Details of the default configuration
|
||||
|
||||
@@ -52,4 +52,4 @@ If you choose to accept the security warning, you will be presented with the log
|
||||
|
||||
If you wish to change the password for the admin user, this can be done using the `MQ_ADMIN_PASSWORD` environment variable. If you supply a PKCS#12 keystore using the `MQ_TLS_KEYSTORE` environment variable, then the web console will be configured to use the certificate inside the keystore for HTTPS operations.
|
||||
|
||||
If you do not wish the web console to run, you can disable it by setting the environment variable `MQ_DISABLE_WEB_CONSOLE` to `true`.
|
||||
If you do not wish the web console to run, you can disable it by setting the environment variable `MQ_ENABLE_EMBEDDED_WEB_SERVER` to `false`.
|
||||
|
||||
@@ -24,6 +24,7 @@ The `runmqserver` command has the following responsibilities:
|
||||
- Works as PID 1, so is responsible for [reaping zombie processes](https://blog.phusion.nl/2015/01/20/docker-and-the-pid-1-zombie-reaping-problem/)
|
||||
* Creating and starting a queue manager
|
||||
* Configuring the queue manager, by running any MQSC scripts found under `/etc/mqm`
|
||||
* Starts the MQ web server (if enabled)
|
||||
* Starting Prometheus metrics generation for the queue manager (if enabled)
|
||||
* Indicates to the `chkmqready` command that configuration is complete, and that normal readiness checking can happen. This is done by writing a file into `/run/runmqserver`
|
||||
|
||||
@@ -36,8 +37,6 @@ The `runmqdevserver` command is added to the MQ Advanced for Developers image on
|
||||
2. Generates MQSC files to put in `/etc/mqm`, based on a template, which is updated with values based on supplied environment variables.
|
||||
3. If requested, it creates TLS key stores under `/run/runmqdevserver`, and configures MQ and the web server to use them
|
||||
|
||||
A special version of `runmqserver` is used in the developer image, which performs extra actions like starting the web server. This is built using the `mqdev` [build constraint](https://golang.org/pkg/go/build/#hdr-Build_Constraints).
|
||||
|
||||
## Prometheus metrics
|
||||
[Prometheus](https://prometheus.io) metrics are generated for the queue manager as follows:
|
||||
|
||||
|
||||
39
docs/security.md
Normal file
39
docs/security.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# Security
|
||||
|
||||
## Container runtime
|
||||
|
||||
### User
|
||||
|
||||
The MQ server image is run using the "mqm" user, with a fixed 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-amd64
|
||||
```
|
||||
|
||||
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-amd64
|
||||
```
|
||||
|
||||
### 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.
|
||||
@@ -6,7 +6,6 @@ You need to ensure you have the following tools installed:
|
||||
* [GNU make](https://www.gnu.org/software/make/)
|
||||
* [Go](https://golang.org/) - only needed for running the tests
|
||||
* [dep](https://github.com/golang/dep) (official Go dependency management tool) - needed to prepare for running the tests
|
||||
* [Helm](https://helm.sh) - only needed for running the Kubernetes tests
|
||||
|
||||
## Running the tests
|
||||
There are two main sets of tests:
|
||||
@@ -25,7 +24,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-amd64 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 +33,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-amd64`:
|
||||
|
||||
```
|
||||
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
|
||||
|
||||
@@ -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`.
|
||||
@@ -96,3 +96,22 @@ docker exec \
|
||||
```
|
||||
|
||||
Using this technique, you can have full control over all aspects of the MQ installation. Note that if you use this technique to make changes to the filesystem, then those changes would be lost if you re-created your container unless you make those changes in volumes.
|
||||
|
||||
## Supplying TLS certificates
|
||||
|
||||
If you wish to supply TLS Certificates that the queue manager and MQ Console should use for TLS operations then you must supply a PKCS#1 or unencrypted PKCS#8 PEM files for both the certificates and private keys in the following directories:
|
||||
|
||||
* `/etc/mqm/pki/keys/<Label>` - for certificates with public and private keys
|
||||
* `/etc/mqm/pki/trust/<index>` - for certificates with only the public key
|
||||
|
||||
For example, if you have an identity certificate you wish to add with the label `mykey` and 2 certificates you wish to add as trusted then you would need to add the files into the following locations where files ending in `.key` contain private keys and `.crt` contain certificates:
|
||||
|
||||
- `/etc/mqm/pki/keys/mykey/tls.key`
|
||||
- `/etc/mqm/pki/keys/mykey/tls.crt`
|
||||
- `/etc/mqm/pki/keys/mykey/ca.crt`
|
||||
- `/etc/mqm/pki/trust/0/tls.crt`
|
||||
- `/etc/mqm/pki/trust/1/tls.crt`
|
||||
|
||||
This can be achieved by either mounting the directories or files into the container when you run it or by baking the files into the correct location in the image.
|
||||
|
||||
If you supply multiple identity certificates then the first label alphabetically will be chosen as the certificate to be used by the MQ Console and the default certificate for the queue manager. If you wish to use a different certificate on the queue manager then you can change the certificate to use at runtime by executing the MQSC command `ALTER QMGR CERTLABL('<newlabel>')`
|
||||
|
||||
19
etc/mqm/15-tls.mqsc.tpl
Normal file
19
etc/mqm/15-tls.mqsc.tpl
Normal file
@@ -0,0 +1,19 @@
|
||||
* © 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.
|
||||
|
||||
* Set the keystore location for the queue manager
|
||||
ALTER QMGR SSLKEYR('{{ .SSLKeyR }}')
|
||||
ALTER QMGR CERTLABL('{{ .CertificateLabel }}')
|
||||
|
||||
8
glide.lock
generated
8
glide.lock
generated
@@ -1,5 +1,5 @@
|
||||
hash: b02555ebf3957ece0ae5ecf132fa4e415a4f66a7f4c27a82d484f4fb78f56e41
|
||||
updated: 2018-07-13T08:50:32.923040349+01:00
|
||||
hash: 6ebd5fb1c39729378c7256da6f312e9699bff1ddff9941d3c8c1ba785e22acfd
|
||||
updated: 2019-05-21T10:38:01.227081+01:00
|
||||
imports:
|
||||
- name: github.com/beorn7/perks
|
||||
version: 3a771d992973f24aa725d07868b467d1ddfceafb
|
||||
@@ -50,4 +50,8 @@ imports:
|
||||
version: 1b2967e3c290b7c545b3db0deeda16e9be4f98a2
|
||||
subpackages:
|
||||
- unix
|
||||
- name: software.sslmate.com/src/go-pkcs12
|
||||
version: 6e380ad96778cc63c6ea17649a9b74224bceafe9
|
||||
subpackages:
|
||||
- internal/rc2
|
||||
testImports: []
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# © Copyright IBM Corporation 2017
|
||||
# © 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.
|
||||
@@ -26,3 +26,5 @@ import:
|
||||
version: 2.0.0
|
||||
- package: github.com/genuinetools/amicontained
|
||||
version: 0.4.0
|
||||
- package: software.sslmate.com/src/go-pkcs12
|
||||
commit: 6e380ad96778cc63c6ea17649a9b74224bceafe9
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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.
|
||||
@@ -12,27 +12,33 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
FROM ubuntu:16.04
|
||||
FROM registry.access.redhat.com/ubi7/ubi-minimal AS mq-explorer
|
||||
|
||||
# The URL to download the MQ installer from in tar.gz format
|
||||
ARG MQ_URL=https://public.dhe.ibm.com/ibmdl/export/pub/software/websphere/messaging/mqadv/mqadv_dev911_ubuntu_x86-64.tar.gz
|
||||
ARG MQ_URL="https://public.dhe.ibm.com/ibmdl/export/pub/software/websphere/messaging/mqadv/mqadv_dev912_linux_x86-64.tar.gz"
|
||||
|
||||
# The MQ packages to install
|
||||
ARG MQ_PACKAGES="ibmmq-explorer"
|
||||
ENV MQ_PACKAGES="MQSeriesRuntime*.rpm MQSeriesJRE*.rpm MQSeriesExplorer*.rpm"
|
||||
|
||||
RUN export DEBIAN_FRONTEND=noninteractive \
|
||||
&& apt-get update \
|
||||
&& apt-get install -y \
|
||||
libgtk2.0-0 \
|
||||
libxtst6
|
||||
ARG MQM_UID=888
|
||||
|
||||
RUN microdnf install -y --nodocs gtk2 libXtst \
|
||||
&& microdnf clean all
|
||||
|
||||
ADD install-mq.sh /usr/local/bin/
|
||||
|
||||
# Install MQ Explorer. To avoid a "text file busy" error here, we sleep before installing.
|
||||
# Need to re-instate the `/var/mqm` directory after installation, to avoid MQ
|
||||
# errors with some commands (e.g. `dspmqver`)
|
||||
RUN chmod u+x /usr/local/bin/install-mq.sh \
|
||||
&& install-mq.sh
|
||||
&& sleep 1 \
|
||||
&& install-mq.sh $MQM_UID \
|
||||
&& rm -rf /var/mqm \
|
||||
&& /opt/mqm/bin/crtmqdir -f -s
|
||||
|
||||
ENV LANG=en_US.UTF-8
|
||||
|
||||
# Run as mqm (999)
|
||||
USER 999
|
||||
# Run as mqm
|
||||
USER $MQM_UID
|
||||
|
||||
ENTRYPOINT ["MQExplorer"]
|
||||
@@ -1,33 +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.
|
||||
|
||||
ARG BASE_IMAGE=mq-sdk:9.1.1.0-x86_64-ubuntu-16.04
|
||||
|
||||
FROM $BASE_IMAGE
|
||||
|
||||
COPY incubating/mq-golang-sdk/install-golang.sh /usr/local/bin
|
||||
|
||||
ENV GO_VERSION=1.10
|
||||
|
||||
ENV PATH="${PATH}:/usr/lib/go-${GO_VERSION}/bin:/go/bin:/usr/local/go/bin" \
|
||||
CGO_CFLAGS="-I/opt/mqm/inc/" \
|
||||
CGO_LDFLAGS_ALLOW="-Wl,-rpath.*" \
|
||||
GOPATH="/go"
|
||||
|
||||
# Install the Go compiler and Git
|
||||
RUN chmod +x /usr/local/bin/install-golang.sh \
|
||||
&& sleep 1 \
|
||||
&& install-golang.sh
|
||||
|
||||
WORKDIR $GOPATH
|
||||
@@ -1,5 +0,0 @@
|
||||
# IBM MQ Software Developer Kit (SDK) with Go
|
||||
|
||||
This image contains the MQ SDK, Git, the Go compiler, and the `build-essential` package (which includes GNU C and C++ compilers plus other essential tools like `make`).
|
||||
|
||||
This image doesn't contain any Go code for MQ. You can add a CGO wrapper for the MQ C client, for example [mq-golang](https://github.com/ibm-messaging/mq-golang), via your vendor directory, or directly using `go get`.
|
||||
@@ -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,19 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
ARG BASE_IMAGE=ubuntu:16.04
|
||||
|
||||
FROM $BASE_IMAGE
|
||||
FROM registry.access.redhat.com/rhscl/devtoolset-7-toolchain-rhel7 AS mq-sdk
|
||||
#FROM docker.io/centos/devtoolset-7-toolchain-centos7 AS mq-sdk
|
||||
|
||||
# The URL to download the MQ installer from in tar.gz format
|
||||
# This assumes an archive containing the MQ Debian (.deb) install packages
|
||||
ARG MQ_URL
|
||||
|
||||
# The packages to install in install-mq.sh
|
||||
ARG MQ_PACKAGES
|
||||
ENV MQ_PACKAGES="MQSeriesRuntime-*.rpm MQSeriesSDK-*.rpm MQSeriesSamples*.rpm"
|
||||
|
||||
ENV MQM_UID=888
|
||||
|
||||
USER 0
|
||||
COPY install-mq.sh /usr/local/bin/
|
||||
|
||||
# Install MQ. To avoid a "text file busy" error here, we sleep before installing.
|
||||
@@ -30,6 +32,7 @@ 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
|
||||
USER 1001
|
||||
@@ -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,8 @@
|
||||
* 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)
|
||||
ALTER LISTENER('SYSTEM.LISTENER.TCP.1') TRPTYPE(TCP) CONTROL(MANUAL)
|
||||
|
||||
* Developer queues
|
||||
DEFINE QLOCAL('DEV.QUEUE.1') REPLACE
|
||||
@@ -50,4 +51,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)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
* © Copyright IBM Corporation 2018
|
||||
* © Copyright IBM Corporation 2018, 2019
|
||||
*
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@@ -13,10 +13,6 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
|
||||
* Set the keystore location for the queue manager
|
||||
ALTER QMGR SSLKEYR('{{ .SSLKeyR }}')
|
||||
ALTER QMGR CERTLABL('{{ .CertificateLabel }}')
|
||||
|
||||
* Set the cipherspec for dev channels
|
||||
ALTER CHANNEL('DEV.APP.SVRCONN') CHLTYPE(SVRCONN) SSLCIPH({{ .SSLCipherSpec }}) SSLCAUTH(OPTIONAL)
|
||||
ALTER CHANNEL('DEV.ADMIN.SVRCONN') CHLTYPE(SVRCONN) SSLCIPH({{ .SSLCipherSpec }}) SSLCAUTH(OPTIONAL)
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
# © Copyright IBM Corporation 2015, 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.
|
||||
|
||||
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
|
||||
|
||||
###############################################################################
|
||||
# Build stage to build Go code
|
||||
###############################################################################
|
||||
FROM $BUILDER_IMAGE as builder
|
||||
ARG IMAGE_REVISION="Not specified"
|
||||
ARG IMAGE_SOURCE="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 ./cmd/runmqdevserver/
|
||||
# Run all unit tests
|
||||
RUN go test -v ./cmd/runmqdevserver/...
|
||||
|
||||
###############################################################################
|
||||
# Main build stage
|
||||
###############################################################################
|
||||
FROM $BASE_IMAGE
|
||||
|
||||
# Enable MQ developer default configuration
|
||||
ENV MQ_DEV=true
|
||||
|
||||
# Default administrator password
|
||||
ENV MQ_ADMIN_PASSWORD=passw0rd
|
||||
|
||||
## Add admin and app users, and set a default password for admin
|
||||
RUN useradd admin -G mqm \
|
||||
&& groupadd mqclient \
|
||||
&& useradd app -G mqclient \
|
||||
&& echo admin:$MQ_ADMIN_PASSWORD | chpasswd
|
||||
|
||||
# Create a directory for runtime data from runmqserver
|
||||
RUN mkdir -p /run/runmqdevserver \
|
||||
&& chown mqm:mqm /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*
|
||||
|
||||
EXPOSE 9443
|
||||
|
||||
ENTRYPOINT ["runmqdevserver"]
|
||||
@@ -1,17 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Change admin password
|
||||
if [ -n "${MQ_ADMIN_PASSWORD}" ]; then
|
||||
echo admin:${MQ_ADMIN_PASSWORD} | chpasswd
|
||||
fi
|
||||
# Change app password
|
||||
if [ -n "${MQ_APP_PASSWORD}" ]; then
|
||||
echo app:${MQ_APP_PASSWORD} | chpasswd
|
||||
fi
|
||||
|
||||
# Delete the MQSC with developer defaults, if requested
|
||||
if [ "${MQ_DEV}" != "true" ]; then
|
||||
rm -f /etc/mqm/dev.mqsc
|
||||
fi
|
||||
|
||||
exec runmqserver
|
||||
38
incubating/mqadvanced-server-dev/install-extra-packages.sh
Normal file
38
incubating/mqadvanced-server-dev/install-extra-packages.sh
Normal file
@@ -0,0 +1,38 @@
|
||||
#!/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 && YUM=true || YUM=false
|
||||
test -f /usr/bin/microdnf && MICRODNF=true || MICRODNF=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 ($YUM); then
|
||||
yum -y install sudo
|
||||
yum -y clean all
|
||||
rm -rf /var/cache/yum/*
|
||||
fi
|
||||
|
||||
if ($MICRODNF); then
|
||||
microdnf install --nodocs sudo
|
||||
microdnf clean all
|
||||
fi
|
||||
@@ -1,7 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<server>
|
||||
<keyStore id="MQWebKeyStore" location="/run/runmqdevserver/tls/key.jks" type="JKS" password="${env.MQ_TLS_PASSPHRASE}"/>
|
||||
<keyStore id="MQWebTrustStore" location="/run/runmqdevserver/tls/trust.jks" type="JKS" password="${env.MQ_TLS_PASSPHRASE}"/>
|
||||
<ssl id="thisSSLConfig" clientAuthenticationSupported="true" keyStoreRef="MQWebKeyStore" trustStoreRef="MQWebTrustStore" sslProtocol="TLSv1.2" serverKeyAlias="devcert"/>
|
||||
<sslDefault sslRef="thisSSLConfig"/>
|
||||
</server>
|
||||
@@ -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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/bash
|
||||
# -*- mode: sh -*-
|
||||
# © Copyright IBM Corporation 2018
|
||||
# © Copyright IBM Corporation 2015, 2019
|
||||
#
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@@ -18,7 +18,9 @@
|
||||
# Fail on any non-zero return code
|
||||
set -ex
|
||||
|
||||
test -f /usr/bin/yum && RHEL=true || RHEL=false
|
||||
test -f /usr/bin/yum && YUM=true || YUM=false
|
||||
test -f /usr/bin/microdnf && MICRODNF=true || MICRODNF=false
|
||||
test -f /usr/bin/rpm && RPM=true || RPM=false
|
||||
test -f /usr/bin/apt-get && UBUNTU=true || UBUNTU=false
|
||||
|
||||
if ($UBUNTU); then
|
||||
@@ -37,37 +39,43 @@ if ($UBUNTU); then
|
||||
# This ensures no unsupported code gets installed, and makes the build faster
|
||||
echo "deb ${APT_URL} ${UBUNTU_CODENAME} main restricted" > /etc/apt/sources.list
|
||||
echo "deb ${APT_URL} ${UBUNTU_CODENAME}-updates main restricted" >> /etc/apt/sources.list
|
||||
echo "deb ${APT_URL} ${UBUNTU_CODENAME}-backports main restricted universe" >> /etc/apt/sources.list;
|
||||
echo "deb ${APT_URL} ${UBUNTU_CODENAME}-security main restricted" >> /etc/apt/sources.list
|
||||
|
||||
# Install additional packages required by MQ, this install process and the runtime scripts
|
||||
apt-get update
|
||||
apt-get install -y --no-install-recommends \
|
||||
golang-${GO_VERSION} \
|
||||
git \
|
||||
ca-certificates
|
||||
fi
|
||||
|
||||
if ($RHEL); then
|
||||
# Install additional packages required by MQ, this install process and the runtime scripts
|
||||
yum -y install \
|
||||
git \
|
||||
bash \
|
||||
bc \
|
||||
ca-certificates \
|
||||
coreutils \
|
||||
curl \
|
||||
debianutils \
|
||||
file \
|
||||
findutils \
|
||||
gawk \
|
||||
grep \
|
||||
libc-bin \
|
||||
mount \
|
||||
passwd \
|
||||
procps \
|
||||
sed \
|
||||
tar \
|
||||
gcc
|
||||
|
||||
cd /tmp
|
||||
curl -LO https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz
|
||||
tar -C /usr/local -xzf go${GO_VERSION}.linux-amd64.tar.gz
|
||||
util-linux
|
||||
fi
|
||||
|
||||
# Remove any orphaned packages
|
||||
$UBUNTU && apt-get autoremove -y
|
||||
if ($RPM); then
|
||||
EXTRA_RPMS="bash bc ca-certificates coreutils file findutils gawk glibc-common grep passwd procps-ng sed shadow-utils tar util-linux which"
|
||||
# Install additional packages required by MQ, this install process and the runtime scripts
|
||||
$YUM && yum -y install --setopt install_weak_deps=false ${EXTRA_RPMS}
|
||||
$MICRODNF && microdnf install --nodocs ${EXTRA_RPMS}
|
||||
fi
|
||||
|
||||
# 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 perl-base --only-upgrade
|
||||
# End of bug fixes
|
||||
|
||||
# Clean up cached files
|
||||
$UBUNTU && rm -rf /var/lib/apt/lists/*
|
||||
$RHEL && yum -y clean all
|
||||
$RHEL && rm -rf /var/cache/yum/*
|
||||
|
||||
# Make the GOLANG directories
|
||||
mkdir -p $GOPATH/src $GOPATH/bin
|
||||
chmod -R 777 $GOPATH
|
||||
$YUM && yum -y clean all
|
||||
$YUM && rm -rf /var/cache/yum/*
|
||||
$MICRODNF && microdnf clean all
|
||||
133
install-mq.sh
133
install-mq.sh
@@ -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,99 +18,27 @@
|
||||
# Fail on any non-zero return code
|
||||
set -ex
|
||||
|
||||
test -f /usr/bin/yum && RHEL=true || RHEL=false
|
||||
mqm_uid=${1:-888}
|
||||
|
||||
test -f /usr/bin/yum && YUM=true || YUM=false
|
||||
test -f /usr/bin/microdnf && MICRODNF=true || MICRODNF=false
|
||||
test -f /usr/bin/rpm && RPM=true || RPM=false
|
||||
test -f /usr/bin/apt-get && UBUNTU=true || UBUNTU=false
|
||||
|
||||
# If MQ_PACKAGES isn't specifically set, then choose a valid set of defaults
|
||||
if [ -z "$MQ_PACKAGES" ]; then
|
||||
$UBUNTU && MQ_PACKAGES="ibmmq-server ibmmq-java ibmmq-jre ibmmq-gskit ibmmq-msg-.* ibmmq-samples ibmmq-ams"
|
||||
$RHEL && MQ_PACKAGES="MQSeriesRuntime-*.rpm MQSeriesServer-*.rpm MQSeriesJava*.rpm MQSeriesJRE*.rpm MQSeriesGSKit*.rpm MQSeriesMsg*.rpm MQSeriesSamples*.rpm MQSeriesAMS-*.rpm"
|
||||
fi
|
||||
|
||||
if ($UBUNTU); then
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
# Use a reduced set of apt repositories.
|
||||
# This ensures no unsupported code gets installed, and makes the build faster
|
||||
source /etc/os-release
|
||||
# Figure out the correct apt URL based on the CPU architecture
|
||||
CPU_ARCH=$(uname -p)
|
||||
if [ ${CPU_ARCH} == "x86_64" ]; then
|
||||
APT_URL="http://archive.ubuntu.com/ubuntu/"
|
||||
else
|
||||
APT_URL="http://ports.ubuntu.com/ubuntu-ports/"
|
||||
fi
|
||||
# Use a reduced set of apt repositories.
|
||||
# This ensures no unsupported code gets installed, and makes the build faster
|
||||
echo "deb ${APT_URL} ${UBUNTU_CODENAME} main restricted" > /etc/apt/sources.list
|
||||
echo "deb ${APT_URL} ${UBUNTU_CODENAME}-updates main restricted" >> /etc/apt/sources.list
|
||||
echo "deb ${APT_URL} ${UBUNTU_CODENAME}-security main restricted" >> /etc/apt/sources.list
|
||||
# Install additional packages required by MQ, this install process and the runtime scripts
|
||||
apt-get update
|
||||
apt-get install -y --no-install-recommends \
|
||||
bash \
|
||||
bc \
|
||||
ca-certificates \
|
||||
coreutils \
|
||||
curl \
|
||||
debianutils \
|
||||
file \
|
||||
findutils \
|
||||
gawk \
|
||||
grep \
|
||||
libc-bin \
|
||||
mount \
|
||||
passwd \
|
||||
procps \
|
||||
sed \
|
||||
tar \
|
||||
util-linux
|
||||
fi
|
||||
|
||||
# Install additional packages required by MQ, this install process and the runtime scripts
|
||||
$RHEL && yum -y install \
|
||||
bash \
|
||||
bc \
|
||||
ca-certificates \
|
||||
coreutils \
|
||||
curl \
|
||||
file \
|
||||
findutils \
|
||||
gawk \
|
||||
glibc-common \
|
||||
grep \
|
||||
passwd \
|
||||
procps-ng \
|
||||
sed \
|
||||
tar \
|
||||
util-linux
|
||||
|
||||
# Download and extract the MQ installation files
|
||||
DIR_EXTRACT=/tmp/mq
|
||||
mkdir -p ${DIR_EXTRACT}
|
||||
cd ${DIR_EXTRACT}
|
||||
curl -LO $MQ_URL
|
||||
tar -zxvf ./*.tar.gz
|
||||
|
||||
# Remove packages only needed by this script
|
||||
$UBUNTU && apt-get purge -y \
|
||||
ca-certificates \
|
||||
curl
|
||||
|
||||
# Note: ca-certificates and curl are installed by default in RHEL
|
||||
|
||||
# Remove any orphaned packages
|
||||
$UBUNTU && apt-get autoremove -y
|
||||
tar -zxf ./*.tar.gz
|
||||
|
||||
# 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)
|
||||
$RHEL && DIR_RPM=$(find ${DIR_EXTRACT} -name "*.rpm" -printf "%h\n" | sort -u | head -1)
|
||||
$RPM && DIR_RPM=$(find ${DIR_EXTRACT} -name "*.rpm" -printf "%h\n" | sort -u | head -1)
|
||||
# Find location of mqlicense.sh
|
||||
MQLICENSE=$(find ${DIR_EXTRACT} -name "mqlicense.sh")
|
||||
|
||||
@@ -122,10 +50,11 @@ $UBUNTU && echo "deb [trusted=yes] file:${DIR_DEB} ./" > /etc/apt/sources.list.d
|
||||
$UBUNTU && apt-get update
|
||||
$UBUNTU && apt-get install -y $MQ_PACKAGES
|
||||
|
||||
$RHEL && cd $DIR_RPM && rpm -ivh $MQ_PACKAGES
|
||||
$RPM && cd $DIR_RPM && rpm -ivh $MQ_PACKAGES
|
||||
|
||||
# Remove 32-bit libraries from 64-bit container
|
||||
find /opt/mqm /var/mqm -type f -exec file {} \; | awk -F: '/ELF 32-bit/{print $1}' | xargs --no-run-if-empty rm -f
|
||||
# The "file" utility isn't installed by default in UBI, so only try this if it's installed
|
||||
which file && find /opt/mqm /var/mqm -type f -exec file {} \; | awk -F: '/ELF 32-bit/{print $1}' | xargs --no-run-if-empty rm -f
|
||||
|
||||
# Remove tar.gz files unpacked by RPM postinst scripts
|
||||
find /opt/mqm -name '*.tar.gz' -delete
|
||||
@@ -137,35 +66,41 @@ find /opt/mqm -name '*.tar.gz' -delete
|
||||
$UBUNTU && rm -f /etc/apt/sources.list.d/IBM_MQ.list
|
||||
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
|
||||
# End of bug fixes
|
||||
|
||||
# Clean up cached files
|
||||
$UBUNTU && rm -rf /var/lib/apt/lists/*
|
||||
$RHEL && yum -y clean all
|
||||
$RHEL && rm -rf /var/cache/yum/*
|
||||
|
||||
# Optional: Update the command prompt with the MQ version
|
||||
$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
|
||||
install --directory --mode 0775 --owner mqm --group root /mnt/mqm-log
|
||||
install --directory --mode 0775 --owner mqm --group root /mnt/mqm-log/log
|
||||
install --directory --mode 0775 --owner mqm --group root /mnt/mqm-data
|
||||
install --directory --mode 0775 --owner mqm --group root /mnt/mqm-data/qmgrs
|
||||
|
||||
# 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
|
||||
sed -i 's/PASS_MIN_LEN\t5/PASS_MIN_LEN\t8/' /etc/login.defs
|
||||
sed -i 's/# minlen = 9/minlen = 8/' /etc/security/pwquality.conf
|
||||
|
||||
$UBUNTU && PAM_FILE=/etc/pam.d/common-password
|
||||
$RHEL && PAM_FILE=/etc/pam.d/password-auth
|
||||
$RPM && PAM_FILE=/etc/pam.d/password-auth
|
||||
sed -i 's/password\t\[success=1 default=ignore\]\tpam_unix\.so obscure sha512/password\t[success=1 default=ignore]\tpam_unix.so obscure sha512 minlen=8/' $PAM_FILE
|
||||
|
||||
# List all the installed packages, for the build log
|
||||
$RPM && rpm -q --all || true
|
||||
$UBUNTU && dpkg --list || true
|
||||
|
||||
# Copy MQ Licenses into the correct location
|
||||
mkdir -p /licenses
|
||||
cp /opt/mqm/licenses/*.txt /licenses/
|
||||
|
||||
128
internal/containerruntime/runtime.go
Normal file
128
internal/containerruntime/runtime.go
Normal file
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
© 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 containerruntime
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
// 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 false
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// ValidMultiInstanceFilesystem returns true if the supplied filesystem type is valid for a multi-instance queue manager
|
||||
func ValidMultiInstanceFilesystem(fsType string) bool {
|
||||
if !SupportedFilesystem(fsType) {
|
||||
return false
|
||||
}
|
||||
// TODO : check for non-shared filesystems & shared filesystems which are known not to work
|
||||
return true
|
||||
}
|
||||
@@ -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 containerruntime
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
@@ -101,24 +99,17 @@ 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
|
||||
}
|
||||
switch t {
|
||||
case "aufs", "overlayfs", "tmpfs":
|
||||
return fmt.Errorf("%v uses unsupported filesystem type: %v", path, t)
|
||||
default:
|
||||
log.Printf("Detected %v has filesystem type '%v'", path, t)
|
||||
return nil
|
||||
return "unknown", nil
|
||||
}
|
||||
return t, nil
|
||||
}
|
||||
@@ -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 containerruntime
|
||||
|
||||
// Dummy version of this function, only for non-Linux systems.
|
||||
// Having this allows unit tests to be run on other platforms (e.g. macOS)
|
||||
103
internal/containerruntimelogger/logruntime.go
Normal file
103
internal/containerruntimelogger/logruntime.go
Normal file
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
© 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 containerruntimelogger
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/ibm-messaging/mq-container/internal/containerruntime"
|
||||
"github.com/ibm-messaging/mq-container/internal/logger"
|
||||
"github.com/ibm-messaging/mq-container/internal/user"
|
||||
)
|
||||
|
||||
// LogContainerDetails logs details about the container runtime
|
||||
func LogContainerDetails(log *logger.Logger) 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// For a multi-instance queue manager - check all required mounts exist & validate filesystem type
|
||||
if os.Getenv("MQ_MULTI_INSTANCE") == "true" {
|
||||
log.Println("Multi-instance queue manager: enabled")
|
||||
reqMounts := []string{"/mnt/mqm", "/mnt/mqm-log", "/mnt/mqm-data"}
|
||||
for _, mountPoint := range reqMounts {
|
||||
if fsType, ok := m[mountPoint]; ok {
|
||||
if !containerruntime.ValidMultiInstanceFilesystem(fsType) {
|
||||
return fmt.Errorf("%v uses filesystem type '%v' which is invalid for a multi-instance queue manager", mountPoint, fsType)
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("Missing required mount '%v' for a multi-instance queue manager", mountPoint)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
58
internal/copy/copy.go
Normal file
58
internal/copy/copy.go
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
© 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 copy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/ibm-messaging/mq-container/internal/filecheck"
|
||||
)
|
||||
|
||||
func CopyFileMode(src, dest string, perm os.FileMode) error {
|
||||
|
||||
err := filecheck.CheckFileSource(src)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open %s for copy: %v", src, err)
|
||||
}
|
||||
|
||||
// #nosec G304 - filename variable 'src' is checked above to ensure it is valid
|
||||
in, err := os.Open(src)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open %s for copy: %v", src, err)
|
||||
}
|
||||
defer in.Close()
|
||||
|
||||
out, err := os.OpenFile(dest, os.O_CREATE|os.O_WRONLY, perm)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open %s for copy: %v", dest, err)
|
||||
}
|
||||
defer out.Close()
|
||||
|
||||
_, err = io.Copy(out, in)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = out.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
// CopyFile copies the specified file
|
||||
func CopyFile(src, dest string) error {
|
||||
return CopyFileMode(src, dest, 0770)
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
// +build !mqdev
|
||||
|
||||
/*
|
||||
© Copyright IBM Corporation 2018
|
||||
© 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.
|
||||
@@ -15,8 +13,25 @@ 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
|
||||
|
||||
func postInit(name string) error {
|
||||
package filecheck
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// CheckFileSource checks the filename is valid
|
||||
func CheckFileSource(fileName string) error {
|
||||
|
||||
absFile, _ := filepath.Abs(fileName)
|
||||
|
||||
prefixes := []string{"bin", "boot", "dev", "lib", "lib64", "proc", "sbin", "sys"}
|
||||
for _, prefix := range prefixes {
|
||||
if strings.HasPrefix(absFile, filepath.Join("/", prefix)) {
|
||||
return fmt.Errorf("Filename resolves to invalid path '%v'", absFile)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
40
internal/filecheck/filecheck_test.go
Normal file
40
internal/filecheck/filecheck_test.go
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
© 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 filecheck
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCheckFileSource(t *testing.T) {
|
||||
|
||||
invalidFilenames := []string{"/bin", "/boot", "/dev", "/lib", "/lib64", "/proc", "/sbin", "/sys", "/bin/myfile", "/boot/mydir/myfile", "/var/../dev", "/var/../lib/myfile"}
|
||||
for _, invalidFilename := range invalidFilenames {
|
||||
err := CheckFileSource(invalidFilename)
|
||||
if err == nil {
|
||||
t.Errorf("Expected to receive an error for filename '%v'", invalidFilename)
|
||||
}
|
||||
}
|
||||
|
||||
validFilenames := []string{"/var", "/mydir/dev", "/mydir/dev/myfile"}
|
||||
for _, validFilename := range validFilenames {
|
||||
err := CheckFileSource(validFilename)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error received: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
@@ -13,12 +13,15 @@ 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 keystore contains code to create and update keystores
|
||||
package keystore
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
@@ -53,13 +56,22 @@ func NewCMSKeyStore(filename, password string) *KeyStore {
|
||||
}
|
||||
}
|
||||
|
||||
// NewPKCS12KeyStore creates a new PKCS12 Key Store, managed by the runmqakm command
|
||||
func NewPKCS12KeyStore(filename, password string) *KeyStore {
|
||||
return &KeyStore{
|
||||
Filename: filename,
|
||||
Password: password,
|
||||
keyStoreType: "p12",
|
||||
command: "/opt/mqm/bin/runmqakm",
|
||||
}
|
||||
}
|
||||
|
||||
// Create a key store, if it doesn't already exist
|
||||
func (ks *KeyStore) Create() error {
|
||||
_, err := os.Stat(ks.Filename)
|
||||
if err == nil {
|
||||
// Keystore already exists so we should refresh it by deleting it.
|
||||
extension := filepath.Ext(ks.Filename)
|
||||
log.Debugf("Refreshing keystore: %v", ks.Filename)
|
||||
if ks.keyStoreType == "cms" {
|
||||
// Only delete these when we are refreshing the kdb keystore
|
||||
stashFile := ks.Filename[0:len(ks.Filename)-len(extension)] + ".sth"
|
||||
@@ -67,23 +79,19 @@ func (ks *KeyStore) Create() error {
|
||||
crlFile := ks.Filename[0:len(ks.Filename)-len(extension)] + ".crl"
|
||||
err = os.Remove(stashFile)
|
||||
if err != nil {
|
||||
log.Errorf("Error removing %s: %v", stashFile, err)
|
||||
return err
|
||||
}
|
||||
err = os.Remove(rdbFile)
|
||||
if err != nil {
|
||||
log.Errorf("Error removing %s: %v", rdbFile, err)
|
||||
return err
|
||||
}
|
||||
err = os.Remove(crlFile)
|
||||
if err != nil {
|
||||
log.Errorf("Error removing %s: %v", crlFile, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
err = os.Remove(ks.Filename)
|
||||
if err != nil {
|
||||
log.Errorf("Error removing %s: %v", ks.Filename, err)
|
||||
return err
|
||||
}
|
||||
} else if !os.IsNotExist(err) {
|
||||
@@ -99,12 +107,10 @@ func (ks *KeyStore) Create() error {
|
||||
|
||||
mqmUID, mqmGID, err := command.LookupMQM()
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
err = os.Chown(ks.Filename, mqmUID, mqmGID)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@@ -114,7 +120,6 @@ func (ks *KeyStore) Create() error {
|
||||
func (ks *KeyStore) CreateStash() error {
|
||||
extension := filepath.Ext(ks.Filename)
|
||||
stashFile := ks.Filename[0:len(ks.Filename)-len(extension)] + ".sth"
|
||||
log.Debugf("TLS stash file: %v", stashFile)
|
||||
_, err := os.Stat(stashFile)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
@@ -127,12 +132,10 @@ func (ks *KeyStore) CreateStash() error {
|
||||
}
|
||||
mqmUID, mqmGID, err := command.LookupMQM()
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
err = os.Chown(stashFile, mqmUID, mqmGID)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@@ -147,6 +150,33 @@ func (ks *KeyStore) Import(inputFile, password string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateSelfSignedCertificate creates a self-signed certificate in the keystore
|
||||
func (ks *KeyStore) CreateSelfSignedCertificate(label, dn string) error {
|
||||
out, _, err := command.Run(ks.command, "-cert", "-create", "-db", ks.Filename, "-pw", ks.Password, "-label", label, "-dn", dn)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error running \"%v -cert -create\": %v %s", ks.command, err, out)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Add adds a CA certificate to the keystore
|
||||
func (ks *KeyStore) Add(inputFile, label string) error {
|
||||
out, _, err := command.Run(ks.command, "-cert", "-add", "-db", ks.Filename, "-type", ks.keyStoreType, "-pw", ks.Password, "-file", inputFile, "-label", label)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error running \"%v -cert -add\": %v %s", ks.command, err, out)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Add adds a CA certificate to the keystore
|
||||
func (ks *KeyStore) AddNoLabel(inputFile string) error {
|
||||
out, _, err := command.Run(ks.command, "-cert", "-add", "-db", ks.Filename, "-type", ks.keyStoreType, "-pw", ks.Password, "-file", inputFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error running \"%v -cert -add\": %v %s", ks.command, err, out)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetCertificateLabels returns the labels of all certificates in the key store
|
||||
func (ks *KeyStore) GetCertificateLabels() ([]string, error) {
|
||||
out, _, err := command.Run(ks.command, "-cert", "-list", "-type", ks.keyStoreType, "-db", ks.Filename, "-pw", ks.Password)
|
||||
@@ -171,9 +201,42 @@ func (ks *KeyStore) GetCertificateLabels() ([]string, error) {
|
||||
|
||||
// RenameCertificate renames the specified certificate
|
||||
func (ks *KeyStore) RenameCertificate(from, to string) error {
|
||||
if ks.command == "/opt/mqm/bin/runmqakm" {
|
||||
// runmqakm can't handle certs with ' in them so just use capicmd
|
||||
cmd := exec.Command("/opt/mqm/gskit8/bin/gsk8capicmd_64", "-cert", "-rename", "-db", ks.Filename, "-pw", ks.Password, "-label", from, "-new_label", to)
|
||||
cmd.Env = append(os.Environ(), "LD_LIBRARY_PATH=/opt/mqm/gskit8/lib64/:/opt/mqm/gskit8/lib")
|
||||
out, _, err := command.RunCmd(cmd)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error running \"%v -cert -rename\": %v %s", "/opt/mqm/gskit8/bin/gsk8capicmd_64", err, out)
|
||||
}
|
||||
} else {
|
||||
out, _, err := command.Run(ks.command, "-cert", "-rename", "-db", ks.Filename, "-pw", ks.Password, "-label", from, "-new_label", to)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error running \"%v -cert -rename\": %v %s", ks.command, err, out)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListCertificates Lists all certificates in the keystore
|
||||
func (ks *KeyStore) ListAllCertificates() ([]string, error) {
|
||||
out, _, err := command.Run(ks.command, "-cert", "-list", "-type", ks.keyStoreType, "-db", ks.Filename, "-pw", ks.Password)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error running \"%v -cert -list\": %v %s", ks.command, err, out)
|
||||
}
|
||||
scanner := bufio.NewScanner(strings.NewReader(out))
|
||||
var labels []string
|
||||
for scanner.Scan() {
|
||||
s := scanner.Text()
|
||||
if strings.HasPrefix(s, "-") || strings.HasPrefix(s, "*-") || strings.HasPrefix(s, "!") {
|
||||
s := strings.TrimLeft(s, "-*!")
|
||||
labels = append(labels, strings.TrimSpace(s))
|
||||
}
|
||||
}
|
||||
err = scanner.Err()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return labels, nil
|
||||
}
|
||||
@@ -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)
|
||||
@@ -110,11 +114,6 @@ func (l *Logger) log(level string, msg string) {
|
||||
l.mutex.Unlock()
|
||||
}
|
||||
|
||||
// LogDirect logs a message directly to stdout
|
||||
func (l *Logger) LogDirect(msg string) {
|
||||
fmt.Println(msg)
|
||||
}
|
||||
|
||||
// Debug logs a line as debug
|
||||
func (l *Logger) Debug(args ...interface{}) {
|
||||
if l.debug {
|
||||
|
||||
@@ -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.
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/ibm-messaging/mq-container/internal/logger"
|
||||
"github.com/ibm-messaging/mq-container/internal/ready"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
@@ -39,6 +40,15 @@ var (
|
||||
// GatherMetrics gathers metrics for the queue manager
|
||||
func GatherMetrics(qmName string, log *logger.Logger) {
|
||||
|
||||
// If running in standby mode - wait until the queue manager becomes active
|
||||
for {
|
||||
active, _ := ready.IsRunningAsActiveQM(qmName)
|
||||
if active {
|
||||
break
|
||||
}
|
||||
time.Sleep(requestTimeout * time.Second)
|
||||
}
|
||||
|
||||
metricsEnabled = true
|
||||
|
||||
err := startMetricsGathering(qmName, log)
|
||||
|
||||
@@ -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.
|
||||
@@ -71,5 +71,8 @@ func GetQueueManager(name string) (*QueueManager, error) {
|
||||
// GetErrorLogDirectory returns the directory holding the error logs for the
|
||||
// specified queue manager
|
||||
func GetErrorLogDirectory(qm *QueueManager) string {
|
||||
if qm.DataPath != "" {
|
||||
return filepath.Join(qm.DataPath, "errors")
|
||||
}
|
||||
return filepath.Join(qm.Prefix, "qmgrs", qm.Directory, "errors")
|
||||
}
|
||||
|
||||
264
internal/mqscredact/mqscredact.go
Normal file
264
internal/mqscredact/mqscredact.go
Normal file
@@ -0,0 +1,264 @@
|
||||
/*
|
||||
© 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 mqscredact
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"io"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
/* List of sensitive MQ Parameters */
|
||||
var sensitiveParameters = []string{"LDAPPWD", "PASSWORD", "SSLCRYP"}
|
||||
|
||||
// redactionString is what sensitive paramters will be replaced with
|
||||
const redactionString = "(*********)"
|
||||
|
||||
func findEndOfParamterString(stringDenoter rune, r *bufio.Reader) string {
|
||||
parameter := ""
|
||||
for {
|
||||
char, _, err := r.ReadRune()
|
||||
if err != nil {
|
||||
return parameter
|
||||
}
|
||||
parameter = parameter + string(char)
|
||||
if char == stringDenoter {
|
||||
break
|
||||
} else if char == '\n' {
|
||||
// Check if we're on a comment line
|
||||
NewLineLoop:
|
||||
for {
|
||||
// Look at next character without moving buffer forwards
|
||||
chars, err := r.Peek(1)
|
||||
if err != nil {
|
||||
return parameter
|
||||
}
|
||||
// Check if we're at the beginning of some data.
|
||||
startOutput, _ := regexp.MatchString(`[^:0-9\s]`, string(chars[0]))
|
||||
if startOutput {
|
||||
// We are at the start, check if we're on a comment line
|
||||
if chars[0] == '*' {
|
||||
// found a comment line. go to the next newline chraracter
|
||||
CommentLoop:
|
||||
for {
|
||||
char, _, err = r.ReadRune()
|
||||
if err != nil {
|
||||
return parameter
|
||||
}
|
||||
parameter = parameter + string(char)
|
||||
if char == '\n' {
|
||||
break CommentLoop
|
||||
}
|
||||
}
|
||||
// Go round again as we're now on a new line
|
||||
continue NewLineLoop
|
||||
}
|
||||
// We've checked for comment and it isn't a comment line so break without moving buffer forwards
|
||||
break NewLineLoop
|
||||
}
|
||||
// Move the buffer forward and try again
|
||||
char, _, _ = r.ReadRune()
|
||||
parameter = parameter + string(char)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return parameter
|
||||
}
|
||||
|
||||
// getParameterString reads from r in order to find the end of the MQSC Parameter value. This is enclosed in ( ).
|
||||
// This function will return what it finds and will increment the reader pointer along as it goes.
|
||||
func getParameterString(r *bufio.Reader) string {
|
||||
// Add the ( in as it will have been dropped before.
|
||||
parameter := "("
|
||||
Loop:
|
||||
for {
|
||||
char, _, err := r.ReadRune()
|
||||
if err != nil {
|
||||
return parameter
|
||||
}
|
||||
|
||||
parameter = parameter + string(char)
|
||||
|
||||
switch char {
|
||||
case ')':
|
||||
break Loop
|
||||
// TODO: Duplicate code..
|
||||
case '\'', '"':
|
||||
parameter = parameter + findEndOfParamterString(char, r)
|
||||
}
|
||||
}
|
||||
|
||||
return parameter
|
||||
}
|
||||
|
||||
func resetAllParameters(currentVerb, originalString *string, lineContinuation, foundGap, parameterNext, redacting, checkComment *bool) {
|
||||
*currentVerb = ""
|
||||
*originalString = ""
|
||||
*lineContinuation = false
|
||||
*foundGap = false
|
||||
*parameterNext = false
|
||||
*redacting = false
|
||||
*checkComment = true
|
||||
}
|
||||
|
||||
// Redact is the main function for redacting sensitive parameters in MQSC strings
|
||||
// It accepts a string and redacts sensitive paramters such as LDAPPWD or PASSWORD
|
||||
func Redact(out string) (string, error) {
|
||||
out = strings.TrimSpace(out)
|
||||
var returnStr, currentVerb, originalString string
|
||||
var lineContinuation, foundGap, parameterNext, redacting, checkComment bool
|
||||
newline := true
|
||||
resetAllParameters(¤tVerb, &originalString, &lineContinuation, &foundGap, ¶meterNext, &redacting, &checkComment)
|
||||
r := bufio.NewReader(strings.NewReader(out))
|
||||
|
||||
MainLoop:
|
||||
for {
|
||||
// We have found a opening ( so use special parameter parsing
|
||||
if parameterNext {
|
||||
parameterStr := getParameterString(r)
|
||||
if !redacting {
|
||||
returnStr = returnStr + parameterStr
|
||||
} else {
|
||||
returnStr = returnStr + redactionString
|
||||
}
|
||||
|
||||
resetAllParameters(¤tVerb, &originalString, &lineContinuation, &foundGap, ¶meterNext, &redacting, &checkComment)
|
||||
}
|
||||
|
||||
// Loop round getting hte next parameter
|
||||
char, _, err := r.ReadRune()
|
||||
if err == io.EOF {
|
||||
if originalString != "" {
|
||||
returnStr = returnStr + originalString
|
||||
}
|
||||
break
|
||||
} else if err != nil {
|
||||
return returnStr, err
|
||||
}
|
||||
|
||||
/* We need to push forward until we find a non-whitespace, digit or colon character */
|
||||
if newline {
|
||||
startOutput, _ := regexp.MatchString(`[^:0-9\s]`, string(char))
|
||||
if !startOutput {
|
||||
originalString = originalString + string(char)
|
||||
continue MainLoop
|
||||
}
|
||||
newline = false
|
||||
}
|
||||
|
||||
switch char {
|
||||
// Found a line continuation character
|
||||
case '+', '-':
|
||||
lineContinuation = true
|
||||
foundGap = false
|
||||
originalString = originalString + string(char)
|
||||
continue MainLoop
|
||||
|
||||
// Found whitespace/new line
|
||||
case '\n':
|
||||
checkComment = true
|
||||
newline = true
|
||||
fallthrough
|
||||
case '\t', '\r', ' ':
|
||||
if !lineContinuation {
|
||||
foundGap = true
|
||||
}
|
||||
originalString = originalString + string(char)
|
||||
continue MainLoop
|
||||
|
||||
// Found a paramter value
|
||||
case '(':
|
||||
parameterNext = true
|
||||
/* Do not continue as we need to do some checks */
|
||||
|
||||
// Found a comment, parse in a special manner
|
||||
case '*':
|
||||
if checkComment {
|
||||
originalString = originalString + string(char)
|
||||
// Loop round until we find the new line character that marks the end of the comment
|
||||
CommentLoop:
|
||||
for {
|
||||
char, _, err := r.ReadRune()
|
||||
if err == io.EOF {
|
||||
if originalString != "" {
|
||||
returnStr = returnStr + originalString
|
||||
}
|
||||
break MainLoop
|
||||
} else if err != nil {
|
||||
return returnStr, err
|
||||
}
|
||||
originalString = originalString + string(char)
|
||||
|
||||
if char == '\n' {
|
||||
break CommentLoop
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//Comment has been read and added to original string, go back to start
|
||||
checkComment = true
|
||||
newline = true
|
||||
continue MainLoop
|
||||
}
|
||||
/* Do not continue as we need to do some checks */
|
||||
|
||||
} //end of switch
|
||||
|
||||
checkComment = false
|
||||
|
||||
if lineContinuation {
|
||||
lineContinuation = false
|
||||
}
|
||||
if foundGap || parameterNext {
|
||||
// we've completed an parameter so check whether it is sensitive
|
||||
currentVerb = strings.ToUpper(currentVerb)
|
||||
|
||||
if isSensitiveCommand(currentVerb) {
|
||||
redacting = true
|
||||
}
|
||||
|
||||
// Add the unedited string to the return string
|
||||
returnStr = returnStr + originalString
|
||||
|
||||
//reset some of the parameters
|
||||
originalString = ""
|
||||
currentVerb = ""
|
||||
foundGap = false
|
||||
lineContinuation = false
|
||||
}
|
||||
|
||||
originalString = originalString + string(char)
|
||||
currentVerb = currentVerb + string(char)
|
||||
}
|
||||
|
||||
return returnStr, nil
|
||||
}
|
||||
|
||||
// isSensitiveCommand checks whether the given string contains a sensitive parameter.
|
||||
// We use contains here because we can't determine whether a line continuation seperates
|
||||
// parts of a parameter or two different parameters.
|
||||
func isSensitiveCommand(command string) bool {
|
||||
for _, v := range sensitiveParameters {
|
||||
if strings.Contains(command, v) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
171
internal/mqscredact/mqscredact_test.go
Normal file
171
internal/mqscredact/mqscredact_test.go
Normal file
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
© 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 mqscredact
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const passwordString = passwordHalf1 + passwordHalf2
|
||||
const passwordHalf1 = "hippo"
|
||||
const passwordHalf2 = "123456"
|
||||
|
||||
var testStrings = [...]string{
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD('" + passwordString + "')",
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD(\"" + passwordString + "\")",
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD ('" + passwordString + "')",
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD\t\t('" + passwordString + "')",
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) ldappwd('" + passwordString + "')",
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LdApPwD('" + passwordString + "')",
|
||||
"DEFINE CHANNEL(CHL) CHLTYPE(SOMETHING) PASSWORD('" + passwordString + "')",
|
||||
"DEFINE CHANNEL(CHL) CHLTYPE(SOMETHING) PASSWORD(\"" + passwordString + "\")",
|
||||
"DEFINE CHANNEL(CHL) CHLTYPE(SOMETHING) PASSWORD ('" + passwordString + "')",
|
||||
"DEFINE CHANNEL(CHL) CHLTYPE(SOMETHING) PASSWORD\t\t('" + passwordString + "')",
|
||||
"DEFINE CHANNEL(CHL) CHLTYPE(SOMETHING) password('" + passwordString + "')",
|
||||
"DEFINE CHANNEL(CHL) CHLTYPE(SOMETHING) pAsSwOrD('" + passwordString + "')",
|
||||
"ALTER QMGR SSLCRYP('" + passwordString + "')",
|
||||
"ALTER QMGR SSLCRYP(\"" + passwordString + "\")",
|
||||
"ALTER QMGR SSLCRYP ('" + passwordString + "')",
|
||||
"ALTER QMGR SSLCRYP\t\t('" + passwordString + "')",
|
||||
"ALTER QMGR sslcryp('" + passwordString + "')",
|
||||
"ALTER QMGR sslCRYP('" + passwordString + "')",
|
||||
|
||||
// Line continuation ones
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD(\"" + passwordHalf1 + "+\n " + passwordHalf2 + "\")",
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD(\"" + passwordHalf1 + "+\n\t" + passwordHalf2 + "\")",
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD(\"" + passwordHalf1 + "+\n\t " + passwordHalf2 + "\")",
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD('" + passwordHalf1 + "+\n " + passwordHalf2 + "')",
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD('" + passwordHalf1 + "+\n\t" + passwordHalf2 + "')",
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD('" + passwordHalf1 + "+\n\t " + passwordHalf2 + "')",
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD(\"" + passwordHalf1 + "-\n" + passwordHalf2 + "\")",
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD('" + passwordHalf1 + "-\n" + passwordHalf2 + "')",
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD(\"" + passwordHalf1 + "+ \n " + passwordHalf2 + "\")",
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD(\"" + passwordHalf1 + "+\t\n " + passwordHalf2 + "\")",
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD(\"" + passwordHalf1 + "- \n" + passwordHalf2 + "\")",
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD(\"" + passwordHalf1 + "-\t\n" + passwordHalf2 + "\")",
|
||||
|
||||
"DEFINE CHANNEL(CHL) CHLTYPE(SOMETHING) PASSWORD(\"" + passwordHalf1 + "+\n " + passwordHalf2 + "\")",
|
||||
|
||||
"ALTER QMGR SSLCRYP(\"" + passwordHalf1 + "+\n " + passwordHalf2 + "\")",
|
||||
|
||||
//edge cases
|
||||
"ALTER QMGR SSLCRYP(\"" + passwordHalf1 + "+\n 123+\n 456\")",
|
||||
"ALTER QMGR SSLCRYP(\"" + passwordHalf1 + "-\n123-\n456\")",
|
||||
|
||||
"ALTER QMGR SSLCRYP(\"" + passwordHalf1 + "+\n 1+\n 2+\n 3+\n 4+\n 5+\n 6\")",
|
||||
"ALTER QMGR SSLCRYP(\"" + passwordHalf1 + "-\n1-\n2-\n3-\n4-\n5-\n6\")",
|
||||
|
||||
"ALTER QMGR SSLCRYP + \n (\"" + passwordHalf1 + "+\n 1+\n 2+\n 3+\n 4+\n 5+\n 6\")",
|
||||
"ALTER QMGR SSLCRYP - \n(\"" + passwordHalf1 + "-\n1-\n2-\n3-\n4-\n5-\n6\")",
|
||||
|
||||
"ALTER QMGR SSL + \n CRYP(\"" + passwordHalf1 + "+\n 1+\n 2+\n 3+\n 4+\n 5+\n 6\")",
|
||||
"ALTER QMGR SSL - \nCRYP(\"" + passwordHalf1 + "-\n1-\n2-\n3-\n4-\n5-\n6\")",
|
||||
|
||||
"ALTER QMGR + \n SSL +\n CRYP(\"" + passwordHalf1 + "+\n 1+\n 2+\n 3+\n 4+\n 5+\n 6\") +\n TEST(1234)",
|
||||
"ALTER QMGR -\nSSL -\nCRYP(\"" + passwordHalf1 + "-\n1-\n2-\n3-\n4-\n5-\n6\") -\nTEST(1234)",
|
||||
|
||||
"ALTER QMGR +\n * COMMENT\n SSL +\n * COMMENT IN MIDDLE\n CRYP('" + passwordString + "')",
|
||||
|
||||
" 1: ALTER CHANNEL(TEST2) CHLTYPE(SDR) PASS+\n : *test comment\n : WORD('" + passwordString + "')",
|
||||
" 2: ALTER CHANNEL(TEST3) CHLTYPE(SDR) PASSWORD('" + passwordHalf1 + "-\n*commentinmiddle with ' \n" + passwordHalf2 + "')",
|
||||
" 3: ALTER CHANNEL(TEST3) CHLTYPE(SDR) PASSWORD('" + passwordHalf1 + "-\n*commentinmiddle with ') \n" + passwordHalf2 + "')",
|
||||
}
|
||||
|
||||
var expected = [...]string{
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD" + redactionString,
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD" + redactionString,
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD " + redactionString,
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD\t\t" + redactionString,
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) ldappwd" + redactionString,
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LdApPwD" + redactionString,
|
||||
"DEFINE CHANNEL(CHL) CHLTYPE(SOMETHING) PASSWORD" + redactionString,
|
||||
"DEFINE CHANNEL(CHL) CHLTYPE(SOMETHING) PASSWORD" + redactionString,
|
||||
"DEFINE CHANNEL(CHL) CHLTYPE(SOMETHING) PASSWORD " + redactionString,
|
||||
"DEFINE CHANNEL(CHL) CHLTYPE(SOMETHING) PASSWORD\t\t" + redactionString,
|
||||
"DEFINE CHANNEL(CHL) CHLTYPE(SOMETHING) password" + redactionString,
|
||||
"DEFINE CHANNEL(CHL) CHLTYPE(SOMETHING) pAsSwOrD" + redactionString,
|
||||
"ALTER QMGR SSLCRYP" + redactionString,
|
||||
"ALTER QMGR SSLCRYP" + redactionString,
|
||||
"ALTER QMGR SSLCRYP " + redactionString,
|
||||
"ALTER QMGR SSLCRYP\t\t" + redactionString,
|
||||
"ALTER QMGR sslcryp" + redactionString,
|
||||
"ALTER QMGR sslCRYP" + redactionString,
|
||||
|
||||
// Line continuation ones
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD" + redactionString,
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD" + redactionString,
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD" + redactionString,
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD" + redactionString,
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD" + redactionString,
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD" + redactionString,
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD" + redactionString,
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD" + redactionString,
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD" + redactionString,
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD" + redactionString,
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD" + redactionString,
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD" + redactionString,
|
||||
|
||||
"DEFINE CHANNEL(CHL) CHLTYPE(SOMETHING) PASSWORD" + redactionString,
|
||||
|
||||
"ALTER QMGR SSLCRYP" + redactionString,
|
||||
|
||||
//edge cases
|
||||
"ALTER QMGR SSLCRYP" + redactionString,
|
||||
"ALTER QMGR SSLCRYP" + redactionString,
|
||||
|
||||
"ALTER QMGR SSLCRYP" + redactionString,
|
||||
"ALTER QMGR SSLCRYP" + redactionString,
|
||||
|
||||
"ALTER QMGR SSLCRYP + \n \t " + redactionString,
|
||||
"ALTER QMGR SSLCRYP - \n " + redactionString,
|
||||
|
||||
"ALTER QMGR SSL + \n CRYP" + redactionString,
|
||||
"ALTER QMGR SSL - \nCRYP" + redactionString,
|
||||
|
||||
"ALTER QMGR + \n SSL +\n CRYP" + redactionString + " +\n TEST(1234)",
|
||||
"ALTER QMGR -\nSSL -\nCRYP" + redactionString + " -\nTEST(1234)",
|
||||
|
||||
"ALTER QMGR +\n * COMMENT\n SSL +\n * COMMENT IN MIDDLE\n CRYP" + redactionString,
|
||||
|
||||
"1: ALTER CHANNEL(TEST2) CHLTYPE(SDR) PASS+\n : *test comment\n : WORD" + redactionString,
|
||||
"2: ALTER CHANNEL(TEST3) CHLTYPE(SDR) PASSWORD" + redactionString,
|
||||
"3: ALTER CHANNEL(TEST3) CHLTYPE(SDR) PASSWORD" + redactionString,
|
||||
}
|
||||
|
||||
// Returns true if the 2 strings are equal ignoring whitespace characters
|
||||
func compareIgnoreWhiteSpace(str1, str2 string) bool {
|
||||
whiteSpaces := [...]string{" ", "\t", "\n", "\r"}
|
||||
for _, w := range whiteSpaces {
|
||||
str1 = strings.Replace(str1, w, "", -1)
|
||||
str2 = strings.Replace(str2, w, "", -1)
|
||||
}
|
||||
|
||||
return str1 == str2
|
||||
}
|
||||
|
||||
func TestAll(t *testing.T) {
|
||||
for i, v := range testStrings {
|
||||
back, _ := Redact(v)
|
||||
if strings.Contains(back, passwordHalf1) || strings.Contains(back, passwordHalf2) || strings.Contains(back, passwordString) {
|
||||
t.Errorf("MAJOR FAIL[%d]: Found an instance of the password. ", i)
|
||||
}
|
||||
|
||||
if !compareIgnoreWhiteSpace(back, expected[i]) {
|
||||
t.Errorf("FAIL[%d]:\nGave :%s\nexpected:%s\ngot :%s", i, v, expected[i], back)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
@@ -13,7 +13,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 mqtemplate contains code to process template files
|
||||
package mqtemplate
|
||||
|
||||
import (
|
||||
"os"
|
||||
@@ -21,11 +23,12 @@ import (
|
||||
"text/template"
|
||||
|
||||
"github.com/ibm-messaging/mq-container/internal/command"
|
||||
"github.com/ibm-messaging/mq-container/internal/logger"
|
||||
)
|
||||
|
||||
// processTemplateFile takes a Go templateFile, and processes it with the
|
||||
// ProcessTemplateFile takes a Go templateFile, and processes it with the
|
||||
// supplied data, writing to destFile
|
||||
func processTemplateFile(templateFile, destFile string, data interface{}) error {
|
||||
func ProcessTemplateFile(templateFile, destFile string, data interface{}, log *logger.Logger) error {
|
||||
// Re-configure channel if app password not set
|
||||
t, err := template.ParseFiles(templateFile)
|
||||
if err != nil {
|
||||
@@ -36,7 +39,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
|
||||
@@ -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.
|
||||
@@ -20,6 +20,9 @@ package ready
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/ibm-messaging/mq-container/internal/command"
|
||||
)
|
||||
|
||||
const fileName string = "/run/runmqserver/ready"
|
||||
@@ -62,3 +65,24 @@ func Check() (bool, error) {
|
||||
}
|
||||
return exists, nil
|
||||
}
|
||||
|
||||
// IsRunningAsActiveQM returns true if the queue manager is running in active mode
|
||||
func IsRunningAsActiveQM(name string) (bool, error) {
|
||||
return isRunningQM(name, "(RUNNING)")
|
||||
}
|
||||
|
||||
// IsRunningAsStandbyQM returns true if the queue manager is running in standby mode
|
||||
func IsRunningAsStandbyQM(name string) (bool, error) {
|
||||
return isRunningQM(name, "(RUNNING AS STANDBY)")
|
||||
}
|
||||
|
||||
func isRunningQM(name string, status string) (bool, error) {
|
||||
out, _, err := command.Run("dspmq", "-n", "-m", name)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if strings.Contains(string(out), status) {
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
610
internal/tls/tls.go
Normal file
610
internal/tls/tls.go
Normal file
@@ -0,0 +1,610 @@
|
||||
/*
|
||||
© 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 tls
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
pwr "math/rand"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"crypto/rand"
|
||||
"crypto/sha512"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
|
||||
"github.com/ibm-messaging/mq-container/internal/filecheck"
|
||||
"github.com/ibm-messaging/mq-container/internal/keystore"
|
||||
pkcs "software.sslmate.com/src/go-pkcs12"
|
||||
)
|
||||
|
||||
// IntegrationDefaultLabel is the default certificate label used by Cloud Integration Platform
|
||||
const IntegrationDefaultLabel = "default"
|
||||
|
||||
// P12TrustStoreName is the name of the PKCS#12 truststore used by the webconsole
|
||||
const P12TrustStoreName = "trust.p12"
|
||||
|
||||
// CMSKeyStoreName is the name of the CMS Keystore used by the queue manager
|
||||
const CMSKeyStoreName = "key.kdb"
|
||||
|
||||
type KeyStoreData struct {
|
||||
Keystore *keystore.KeyStore
|
||||
Password string
|
||||
TrustedCerts []*pem.Block
|
||||
KnownFingerPrints []string
|
||||
KeyLabels []string
|
||||
}
|
||||
|
||||
type P12KeyFiles struct {
|
||||
Keystores []string
|
||||
Password string
|
||||
}
|
||||
|
||||
func getCertFingerPrint(block *pem.Block) (string, error) {
|
||||
// Add to future truststore and known certs (if not already there)
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("could not parse x509 certificate: %v", err)
|
||||
}
|
||||
sha512Sum := sha512.Sum512(cert.Raw)
|
||||
sha512str := string(sha512Sum[:])
|
||||
|
||||
return sha512str, nil
|
||||
}
|
||||
|
||||
// Add to Keystores known certs (if not already there) and add to the
|
||||
// Keystore if "addToKeystore" is true.
|
||||
func addCertToKeyData(block *pem.Block, keyData *KeyStoreData, addToKeystore bool) error {
|
||||
sha512str, err := getCertFingerPrint(block)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
known := false
|
||||
for _, fingerprint := range keyData.KnownFingerPrints {
|
||||
if fingerprint == sha512str {
|
||||
known = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !known {
|
||||
// Sometimes we don't want to add to the keystore trust here.
|
||||
// For example if it will be imported with the key later.
|
||||
if addToKeystore {
|
||||
keyData.TrustedCerts = append(keyData.TrustedCerts, block)
|
||||
}
|
||||
keyData.KnownFingerPrints = append(keyData.KnownFingerPrints, sha512str)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Generates a random 12 character password from the characters a-z, A-Z, 0-9.
|
||||
func generateRandomPassword() string {
|
||||
pwr.Seed(time.Now().Unix())
|
||||
validChars := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
|
||||
validcharArray := []byte(validChars)
|
||||
password := ""
|
||||
for i := 0; i < 12; i++ {
|
||||
password = password + string(validcharArray[pwr.Intn(len(validcharArray))])
|
||||
}
|
||||
|
||||
return password
|
||||
}
|
||||
|
||||
// Creates the PKCS#12 Truststore and the CMS Keystore.
|
||||
func generateAllStores(dir string) (KeyStoreData, KeyStoreData, error) {
|
||||
var cmsKeystore, p12TrustStore KeyStoreData
|
||||
pw := generateRandomPassword()
|
||||
|
||||
cmsKeystore.Password = pw
|
||||
p12TrustStore.Password = pw
|
||||
|
||||
// Create the keystore Directory (if it doesn't already exist)
|
||||
// #nosec G301 - write group permissions are required
|
||||
err := os.MkdirAll(dir, 0770)
|
||||
if err != nil {
|
||||
return cmsKeystore, p12TrustStore, fmt.Errorf("Failed to create keystore directory: %v", err)
|
||||
}
|
||||
|
||||
p12TrustStore.Keystore = keystore.NewPKCS12KeyStore(filepath.Join(dir, P12TrustStoreName), p12TrustStore.Password)
|
||||
err = p12TrustStore.Keystore.Create()
|
||||
if err != nil {
|
||||
return cmsKeystore, p12TrustStore, fmt.Errorf("Failed to create PKCS#12 TrustStore: %v", err)
|
||||
}
|
||||
|
||||
cmsKeystore.Keystore = keystore.NewCMSKeyStore(filepath.Join(dir, CMSKeyStoreName), cmsKeystore.Password)
|
||||
err = cmsKeystore.Keystore.Create()
|
||||
if err != nil {
|
||||
return cmsKeystore, p12TrustStore, fmt.Errorf("Failed to create CMS KeyStore: %v", err)
|
||||
}
|
||||
|
||||
return cmsKeystore, p12TrustStore, nil
|
||||
}
|
||||
|
||||
// processKeys walks through the keyDir directory and imports any keys it finds to individual PKCS#12 keystores
|
||||
// and the CMS KeyStore. The label it uses is the name of the directory if finds the keys in.
|
||||
func processKeys(keyDir, outputDir string, cmsKeyDB, p12TrustDB *KeyStoreData) (string, P12KeyFiles, error) {
|
||||
var p12s P12KeyFiles
|
||||
var firstLabel string
|
||||
|
||||
pwToUse := cmsKeyDB.Password
|
||||
p12s.Password = pwToUse
|
||||
trustStoreReserveredName := P12TrustStoreName[0 : len(P12TrustStoreName)-len(filepath.Ext(P12TrustStoreName))]
|
||||
keyList, err := ioutil.ReadDir(keyDir)
|
||||
|
||||
if err == nil && len(keyList) > 0 {
|
||||
// Found some keys, verify the contents
|
||||
for _, key := range keyList {
|
||||
keys, _ := ioutil.ReadDir(filepath.Join(keyDir, key.Name()))
|
||||
keyLabel := key.Name()
|
||||
if keyLabel == trustStoreReserveredName {
|
||||
return firstLabel, p12s, fmt.Errorf("Found key with same label set as same name as truststore(%s). This is not allowed", trustStoreReserveredName)
|
||||
}
|
||||
|
||||
keyfilename := ""
|
||||
var keyfile interface{}
|
||||
var certFile *x509.Certificate
|
||||
var caFile []*x509.Certificate
|
||||
|
||||
// find the keyfile name
|
||||
for _, a := range keys {
|
||||
if strings.HasSuffix(a.Name(), ".key") {
|
||||
// #nosec G304 - filename variable is derived from contents of 'keyDir' which is a defined constant
|
||||
keyFile, err := ioutil.ReadFile(filepath.Join(keyDir, key.Name(), a.Name()))
|
||||
if err != nil {
|
||||
return firstLabel, p12s, fmt.Errorf("Could not read keyfile %s: %v", filepath.Join(keyDir, key.Name(), a.Name()), err)
|
||||
}
|
||||
block, _ := pem.Decode(keyFile)
|
||||
if block == nil {
|
||||
return firstLabel, p12s, fmt.Errorf("Could not decode keyfile %s: pem.Decode returned nil", filepath.Join(keyDir, key.Name(), a.Name()))
|
||||
}
|
||||
|
||||
//Test whether it is PKCS1
|
||||
keyfile, err = x509.ParsePKCS1PrivateKey(block.Bytes)
|
||||
if err != nil {
|
||||
// Before we fail check whether it is PKCS8
|
||||
keyfile, err = x509.ParsePKCS8PrivateKey(block.Bytes)
|
||||
if err != nil {
|
||||
fmt.Printf("key %s ParsePKCS1/8PrivateKey ERR: %v\n", filepath.Join(keyDir, key.Name(), a.Name()), err)
|
||||
return firstLabel, p12s, err
|
||||
}
|
||||
//It was PKCS8 afterall
|
||||
}
|
||||
keyfilename = a.Name()
|
||||
}
|
||||
}
|
||||
if keyfile == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// Find out what the keyfile was called without the extension
|
||||
prefix := keyfilename[0 : len(keyfilename)-len(filepath.Ext(keyfilename))]
|
||||
|
||||
for _, a := range keys {
|
||||
if strings.HasSuffix(a.Name(), ".key") {
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(a.Name(), prefix) && strings.HasSuffix(a.Name(), ".crt") {
|
||||
// #nosec G304 - filename variable is derived from contents of 'keyDir' which is a defined constant
|
||||
cert, err := ioutil.ReadFile(filepath.Join(keyDir, key.Name(), a.Name()))
|
||||
if err != nil {
|
||||
return firstLabel, p12s, fmt.Errorf("Could not read file %s: %v", filepath.Join(keyDir, key.Name(), a.Name()), err)
|
||||
}
|
||||
block, _ := pem.Decode(cert)
|
||||
if block == nil {
|
||||
return firstLabel, p12s, fmt.Errorf("Could not decode certificate %s: pem.Decode returned nil", filepath.Join(keyDir, key.Name(), a.Name()))
|
||||
}
|
||||
certFile, err = x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
return firstLabel, p12s, fmt.Errorf("Could not parse certificate %s: %v", filepath.Join(keyDir, key.Name(), a.Name()), err)
|
||||
}
|
||||
// Add to the dup list for the CMS keystore but not the PKCS#12 Truststore
|
||||
err = addCertToKeyData(block, cmsKeyDB, false)
|
||||
|
||||
} else if strings.HasSuffix(a.Name(), ".crt") {
|
||||
// #nosec G304 - filename variable is derived from contents of 'keyDir' which is a defined constant
|
||||
remainder, err := ioutil.ReadFile(filepath.Join(keyDir, key.Name(), a.Name()))
|
||||
if err != nil {
|
||||
return firstLabel, p12s, fmt.Errorf("Could not read file %s: %v", filepath.Join(keyDir, key.Name(), a.Name()), err)
|
||||
}
|
||||
|
||||
for string(remainder) != "" {
|
||||
var block *pem.Block
|
||||
block, remainder = pem.Decode(remainder)
|
||||
// If we can't decode the CA certificate then just exit.
|
||||
if block == nil {
|
||||
break
|
||||
}
|
||||
|
||||
// Add to the dup list for the CMS keystore
|
||||
err = addCertToKeyData(block, cmsKeyDB, false)
|
||||
|
||||
// Add to the p12 truststore
|
||||
err = addCertToKeyData(block, p12TrustDB, true)
|
||||
|
||||
caCert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
return firstLabel, p12s, fmt.Errorf("Could not parse CA certificate %s: %v", filepath.Join(keyDir, key.Name(), a.Name()), err)
|
||||
}
|
||||
|
||||
caFile = append(caFile, caCert)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create p12 keystore
|
||||
file, err := pkcs.Encode(rand.Reader, keyfile, certFile, caFile, pwToUse)
|
||||
if err != nil {
|
||||
return firstLabel, p12s, fmt.Errorf("Could not encode PKCS#12 Keystore %s: %v", keyLabel+".p12", err)
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(filepath.Join(outputDir, keyLabel+".p12"), file, 0644)
|
||||
if err != nil {
|
||||
return firstLabel, p12s, fmt.Errorf("Could not write PKCS#12 Keystore %s: %v", filepath.Join(outputDir, keyLabel+".p12"), err)
|
||||
}
|
||||
|
||||
p12s.Keystores = append(p12s.Keystores, keyLabel+".p12")
|
||||
|
||||
// Add to the CMS keystore
|
||||
err = cmsKeyDB.Keystore.Import(filepath.Join(outputDir, keyLabel+".p12"), pwToUse)
|
||||
if err != nil {
|
||||
return firstLabel, p12s, fmt.Errorf("Could not import keys from %s into CMS Keystore: %v", filepath.Join(outputDir, keyLabel+".p12"), err)
|
||||
}
|
||||
|
||||
// Relabel it
|
||||
allLabels, err := cmsKeyDB.Keystore.GetCertificateLabels()
|
||||
if err != nil {
|
||||
return firstLabel, p12s, fmt.Errorf("Could not list keys in CMS Keystore: %v", err)
|
||||
}
|
||||
relabelled := false
|
||||
for _, cl := range allLabels {
|
||||
found := false
|
||||
for _, kl := range cmsKeyDB.KeyLabels {
|
||||
if strings.Trim(cl, "\"") == kl {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
// This is the one to rename
|
||||
err = cmsKeyDB.Keystore.RenameCertificate(strings.Trim(cl, "\""), keyLabel)
|
||||
if err != nil {
|
||||
return firstLabel, p12s, err
|
||||
}
|
||||
relabelled = true
|
||||
cmsKeyDB.KeyLabels = append(cmsKeyDB.KeyLabels, keyLabel)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !relabelled {
|
||||
return firstLabel, p12s, fmt.Errorf("Unable to find the added key for %s in CMS keystore", keyLabel)
|
||||
}
|
||||
|
||||
// First key found so mark it as the one to use with the queue manager.
|
||||
if firstLabel == "" {
|
||||
firstLabel = keyLabel
|
||||
}
|
||||
}
|
||||
}
|
||||
return firstLabel, p12s, nil
|
||||
}
|
||||
|
||||
// processTrustCertificates walks through the trustDir directory and adds any certificates it finds
|
||||
// to the PKCS#12 Truststore and the CMS KeyStore as long as has not already been added.
|
||||
func processTrustCertificates(trustDir string, cmsKeyDB, p12TrustDB *KeyStoreData) error {
|
||||
certList, err := ioutil.ReadDir(trustDir)
|
||||
if err == nil && len(certList) > 0 {
|
||||
// Found some keys, verify the contents
|
||||
for _, cert := range certList {
|
||||
certs, _ := ioutil.ReadDir(filepath.Join(trustDir, cert.Name()))
|
||||
for _, a := range certs {
|
||||
if strings.HasSuffix(a.Name(), ".crt") {
|
||||
// #nosec G304 - filename variable is derived from contents of 'trustDir' which is a defined constant
|
||||
remainder, err := ioutil.ReadFile(filepath.Join(trustDir, cert.Name(), a.Name()))
|
||||
if err != nil {
|
||||
return fmt.Errorf("Could not read file %s: %v", filepath.Join(trustDir, cert.Name(), a.Name()), err)
|
||||
}
|
||||
|
||||
for string(remainder) != "" {
|
||||
var block *pem.Block
|
||||
block, remainder = pem.Decode(remainder)
|
||||
if block == nil {
|
||||
break
|
||||
}
|
||||
|
||||
// Add to the CMS keystore
|
||||
err = addCertToKeyData(block, cmsKeyDB, true)
|
||||
|
||||
// Add to the p12 truststore
|
||||
err = addCertToKeyData(block, p12TrustDB, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// We've potentially created two lists of certificates to import. Add them both to relevant Truststores
|
||||
if len(p12TrustDB.TrustedCerts) > 0 {
|
||||
// Do P12 TrustStore first
|
||||
temporaryPemFile := filepath.Join("/tmp", "trust.pem")
|
||||
_, err := os.Stat(temporaryPemFile)
|
||||
if err == nil {
|
||||
err = os.Remove(temporaryPemFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Could not remove file %v: %v", temporaryPemFile, err)
|
||||
}
|
||||
}
|
||||
|
||||
err = writeCertsToFile(temporaryPemFile, p12TrustDB.TrustedCerts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = p12TrustDB.Keystore.AddNoLabel(temporaryPemFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Could not add certificates to PKCS#12 Truststore: %v", err)
|
||||
}
|
||||
|
||||
// We need to relabel everything because liberty doesn't play nicely with autolabelled certs
|
||||
allCerts, err := p12TrustDB.Keystore.ListAllCertificates()
|
||||
if err != nil || len(allCerts) <= 0 {
|
||||
return fmt.Errorf("Could not get any certificates from PKCS#12 Truststore: %v", err)
|
||||
}
|
||||
|
||||
for i, cert := range allCerts {
|
||||
cert = strings.Trim(cert, "\"")
|
||||
cert = strings.TrimSpace(cert)
|
||||
newLabel := fmt.Sprintf("Trust%d", i)
|
||||
|
||||
err = p12TrustDB.Keystore.RenameCertificate(cert, newLabel)
|
||||
if err != nil || len(allCerts) <= 0 {
|
||||
return fmt.Errorf("Could not rename certificate %s to %s in PKCS#12 Truststore: %v", cert, newLabel, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(cmsKeyDB.TrustedCerts) > 0 {
|
||||
// Now the CMS Keystore
|
||||
temporaryPemFile := filepath.Join("/tmp", "cmsTrust.pem")
|
||||
_, err := os.Stat(temporaryPemFile)
|
||||
if err == nil {
|
||||
err = os.Remove(temporaryPemFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Could not remove file %v: %v", temporaryPemFile, err)
|
||||
}
|
||||
}
|
||||
|
||||
err = writeCertsToFile(temporaryPemFile, cmsKeyDB.TrustedCerts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = cmsKeyDB.Keystore.AddNoLabel(temporaryPemFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Could not add certificates to CMS keystore: %v", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Writes a given list of certificates to a file.
|
||||
func writeCertsToFile(file string, certs []*pem.Block) error {
|
||||
f, err := os.Create(file)
|
||||
if err != nil {
|
||||
return fmt.Errorf("writeCertsToFile: Could not create file %s: %v", file, err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
w := bufio.NewWriter(f)
|
||||
|
||||
for i, c := range certs {
|
||||
err := pem.Encode(w, c)
|
||||
if err != nil {
|
||||
return fmt.Errorf("writeCertsToFile: Could not encode certificate number %d: %v", i, err)
|
||||
}
|
||||
err = w.Flush()
|
||||
if err != nil {
|
||||
return fmt.Errorf("writeCertsToFile: Failed to write to file %s: %v", file, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ConfigureTLSKeystores sets up the TLS Trust and Keystores for use
|
||||
func ConfigureTLSKeystores(keyDir, certDir, outputDir string) (string, KeyStoreData, KeyStoreData, P12KeyFiles, error) {
|
||||
var returnLabel, label string
|
||||
var cmsKeyDB, p12TrustDB KeyStoreData
|
||||
var keyFiles P12KeyFiles
|
||||
var err error
|
||||
|
||||
cmsKeyDB, p12TrustDB, err = generateAllStores(outputDir)
|
||||
if err != nil {
|
||||
return returnLabel, cmsKeyDB, p12TrustDB, keyFiles, err
|
||||
}
|
||||
|
||||
returnLabel, err = expandOldTLSVariable(keyDir, outputDir, &cmsKeyDB, &p12TrustDB)
|
||||
if err != nil {
|
||||
return returnLabel, cmsKeyDB, p12TrustDB, keyFiles, err
|
||||
}
|
||||
|
||||
label, keyFiles, err = processKeys(keyDir, outputDir, &cmsKeyDB, &p12TrustDB)
|
||||
if err != nil {
|
||||
return returnLabel, cmsKeyDB, p12TrustDB, keyFiles, err
|
||||
}
|
||||
if returnLabel == "" {
|
||||
returnLabel = label
|
||||
}
|
||||
|
||||
err = processTrustCertificates(certDir, &cmsKeyDB, &p12TrustDB)
|
||||
if err != nil {
|
||||
return returnLabel, cmsKeyDB, p12TrustDB, keyFiles, err
|
||||
}
|
||||
|
||||
return returnLabel, cmsKeyDB, p12TrustDB, keyFiles, err
|
||||
}
|
||||
|
||||
// This function supports the old mechanism of importing certificates supplied by the MQ_TLS_KEYSTORE envvar
|
||||
func expandOldTLSVariable(keyDir, outputDir string, cmsKeyDB, p12TrustDB *KeyStoreData) (string, error) {
|
||||
// TODO: Change this or find a way to set it
|
||||
outputDirName := "acopiedcertificate"
|
||||
|
||||
// Check whether the old variable is set. If not exit quietly
|
||||
keyfile := os.Getenv("MQ_TLS_KEYSTORE")
|
||||
if keyfile == "" {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// There is a file to read and process
|
||||
keyfilepw := os.Getenv("MQ_TLS_PASSPHRASE")
|
||||
|
||||
if !strings.HasSuffix(keyfile, ".p12") {
|
||||
return "", fmt.Errorf("MQ_TLS_KEYSTORE (%s) does not point to a PKCS#12 file ending with the suffix .p12", keyfile)
|
||||
}
|
||||
|
||||
_, err := os.Stat(keyfile)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("File %s referenced by MQ_TLS_KEYSTORE does not exist", keyfile)
|
||||
}
|
||||
|
||||
err = filecheck.CheckFileSource(keyfile)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("File %s referenced by MQ_TLS_KEYSTORE is invalid: %v", keyfile, err)
|
||||
}
|
||||
|
||||
// #nosec G304 - filename variable 'keyfile' is checked above to ensure it is valid
|
||||
readkey, err := ioutil.ReadFile(keyfile)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Failed to read %s: %v", keyfile, err)
|
||||
}
|
||||
|
||||
// File has been checked and read, decode it.
|
||||
pk, cert, cas, err := pkcs.DecodeChain(readkey, keyfilepw)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Failed to decode %s: %v", keyfile, err)
|
||||
}
|
||||
|
||||
// Find a directory name that doesn't exist
|
||||
for {
|
||||
_, err := os.Stat(filepath.Join(keyDir, outputDirName))
|
||||
if err == nil {
|
||||
outputDirName = outputDirName + "0"
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
//Bceause they supplied this certificate using the old method we should use this for qm & webconsole
|
||||
overrideLabel := outputDirName
|
||||
|
||||
// Write out the certificate for the private key
|
||||
if cert != nil {
|
||||
block := pem.Block{
|
||||
Type: "CERTIFICATE",
|
||||
Headers: nil,
|
||||
Bytes: cert.Raw,
|
||||
}
|
||||
err = addCertToKeyData(&block, cmsKeyDB, false)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("expandOldTLSVariable: Failed to add cert to CMS Keystore duplicate list: %v", err)
|
||||
}
|
||||
err = addCertToKeyData(&block, p12TrustDB, true)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("expandOldTLSVariable: Failed to add cert to P12 Truststore duplicate list: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// now write out all the ca certificates
|
||||
if cas != nil || len(cas) > 0 {
|
||||
for i, c := range cas {
|
||||
block := pem.Block{
|
||||
Type: "CERTIFICATE",
|
||||
Headers: nil,
|
||||
Bytes: c.Raw,
|
||||
}
|
||||
|
||||
// Add to the dup list for the CMS keystore
|
||||
err = addCertToKeyData(&block, cmsKeyDB, false)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("expandOldTLSVariable: Failed to add CA cert %d to CMS Keystore duplicate list: %v", i, err)
|
||||
}
|
||||
|
||||
// Add to the p12 truststore
|
||||
err = addCertToKeyData(&block, p12TrustDB, true)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("expandOldTLSVariable: Failed to add CA cert %d to P12 Truststore duplicate list: %v", i, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now we've handled the certificates copy the keystore into place
|
||||
destination := filepath.Join(outputDir, outputDirName+".p12")
|
||||
|
||||
// Create p12 keystore
|
||||
file, err := pkcs.Encode(rand.Reader, pk, cert, cas, p12TrustDB.Password)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Failed to re-encode p12 keystore: %v", err)
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(destination, file, 0644)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Failed to write p12 keystore: %v", err)
|
||||
}
|
||||
|
||||
// Add to the CMS keystore
|
||||
err = cmsKeyDB.Keystore.Import(destination, p12TrustDB.Password)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Failed to import p12 keystore %s: %v", destination, err)
|
||||
}
|
||||
|
||||
if pk != nil {
|
||||
// Relabel the key
|
||||
allLabels, err := cmsKeyDB.Keystore.GetCertificateLabels()
|
||||
if err != nil {
|
||||
fmt.Printf("cms GetCertificateLabels: %v\n", err)
|
||||
return "", err
|
||||
}
|
||||
relabelled := false
|
||||
for _, cl := range allLabels {
|
||||
found := false
|
||||
for _, kl := range cmsKeyDB.KeyLabels {
|
||||
if strings.Trim(cl, "\"") == kl {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
// This is the one to rename
|
||||
err = cmsKeyDB.Keystore.RenameCertificate(strings.Trim(cl, "\""), outputDirName)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
relabelled = true
|
||||
cmsKeyDB.KeyLabels = append(cmsKeyDB.KeyLabels, outputDirName)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !relabelled {
|
||||
return "", fmt.Errorf("Unable to find the added key in CMS keystore")
|
||||
}
|
||||
}
|
||||
|
||||
return overrideLabel, nil
|
||||
}
|
||||
81
internal/user/user.go
Normal file
81
internal/user/user.go
Normal 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
|
||||
}
|
||||
28
manifests/dockerhub/manifest-9.1.2-UBI.yaml
Normal file
28
manifests/dockerhub/manifest-9.1.2-UBI.yaml
Normal file
@@ -0,0 +1,28 @@
|
||||
# © 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.
|
||||
|
||||
image: ibmcom/mq:9.1.2.0-UBI
|
||||
manifests:
|
||||
- image: ibmcom/mq:9.1.2.0-UBI-amd64
|
||||
platform:
|
||||
architecture: amd64
|
||||
os: linux
|
||||
- image: ibmcom/mq:9.1.2.0-UBI-ppc64le
|
||||
platform:
|
||||
architecture: ppc64le
|
||||
os: linux
|
||||
- image: ibmcom/mq:9.1.2.0-UBI-s390x
|
||||
platform:
|
||||
architecture: s390x
|
||||
os: linux
|
||||
@@ -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
|
||||
@@ -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,16 +14,15 @@
|
||||
|
||||
image: ibmcom/mq:latest
|
||||
manifests:
|
||||
- image: ibmcom/mq:9.1.1.0-x86_64
|
||||
- image: ibmcom/mq:9.1.2.0-UBI-amd64
|
||||
platform:
|
||||
architecture: amd64
|
||||
os: linux
|
||||
- image: ibmcom/mq:9.1.1.0-ppc64le
|
||||
- image: ibmcom/mq:9.1.2.0-UBI-ppc64le
|
||||
platform:
|
||||
architecture: ppc64le
|
||||
os: linux
|
||||
- image: ibmcom/mq:9.1.1.0-s390x
|
||||
- image: ibmcom/mq:9.1.2.0-UBI-s390x
|
||||
platform:
|
||||
architecture: s390x
|
||||
os: linux
|
||||
|
||||
|
||||
28
manifests/dockerstore/manifest-9.1.2-UBI.yaml
Normal file
28
manifests/dockerstore/manifest-9.1.2-UBI.yaml
Normal file
@@ -0,0 +1,28 @@
|
||||
# © 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.
|
||||
|
||||
image: ibmcorp/mqadvanced-server-dev:9.1.2.0-UBI
|
||||
manifests:
|
||||
- image: ibmcorp/mqadvanced-server-dev:9.1.2.0-UBI-amd64
|
||||
platform:
|
||||
architecture: amd64
|
||||
os: linux
|
||||
- image: ibmcorp/mqadvanced-server-dev:9.1.2.0-UBI-ppc64le
|
||||
platform:
|
||||
architecture: ppc64le
|
||||
os: linux
|
||||
- image: ibmcorp/mqadvanced-server-dev:9.1.2.0-UBI-s390x
|
||||
platform:
|
||||
architecture: s390x
|
||||
os: linux
|
||||
29
manifests/dockerstore/manifest-9.1.2.yaml
Normal file
29
manifests/dockerstore/manifest-9.1.2.yaml
Normal 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
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
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.
|
||||
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.
|
||||
|
||||
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.
|
||||
WARNING: The code in this directory is not currently in use, pending deletion. The MQ container is now built using a Red Hat UBI image, using "podman build".
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,50 @@ 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
|
||||
|
||||
# Copy web XML files
|
||||
cp -R 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
|
||||
@@ -114,8 +145,9 @@ fi
|
||||
buildah config \
|
||||
--port 1414/tcp \
|
||||
--port 9157/tcp \
|
||||
--port 9443/tcp \
|
||||
--os linux \
|
||||
--label architecture=x86_64 \
|
||||
--label architecture=amd64 \
|
||||
--label io.openshift.tags="$OSTAG" \
|
||||
--label io.k8s.display-name="$DISNAME" \
|
||||
--label io.k8s.description="IBM MQ is messaging middleware that simplifies and accelerates the integration of diverse applications and business data across multiple platforms. It uses message queues to facilitate the exchanges of information and offers a single messaging solution for cloud, mobile, Internet of Things (IoT) and on-premises environments." \
|
||||
@@ -133,7 +165,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
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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,36 @@ 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
|
||||
cp -R 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
|
||||
@@ -85,7 +93,7 @@ buildah config \
|
||||
--port 9157/tcp \
|
||||
--port 9443/tcp \
|
||||
--os linux \
|
||||
--label architecture=x86_64 \
|
||||
--label architecture=amd64 \
|
||||
--label io.openshift.tags="mq messaging developer" \
|
||||
--label io.k8s.display-name="IBM MQ Advanced Server Developer Edition" \
|
||||
--label io.k8s.description="IBM MQ is messaging middleware that simplifies and accelerates the integration of diverse applications and business data across multiple platforms. It uses message queues to facilitate the exchanges of information and offers a single messaging solution for cloud, mobile, Internet of Things (IoT) and on-premises environments." \
|
||||
@@ -105,7 +113,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
|
||||
|
||||
30
mq-advanced-server-rhel/writePackages.sh
Executable file
30
mq-advanced-server-rhel/writePackages.sh
Executable 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
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/docker/go-connections"
|
||||
version = "0.3.0"
|
||||
version = "0.4.0"
|
||||
|
||||
[prune]
|
||||
go-tests = true
|
||||
|
||||
@@ -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.
|
||||
@@ -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)
|
||||
@@ -135,7 +138,7 @@ func TestDevWebDisabled(t *testing.T) {
|
||||
Env: []string{
|
||||
"LICENSE=accept",
|
||||
"MQ_QMGR_NAME=qm1",
|
||||
"MQ_DISABLE_WEB_CONSOLE=true",
|
||||
"MQ_ENABLE_EMBEDDED_WEB_SERVER=false",
|
||||
},
|
||||
}
|
||||
id := runContainer(t, cli, &containerConfig)
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
@@ -24,7 +24,6 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -38,6 +37,7 @@ import (
|
||||
|
||||
func TestLicenseNotSet(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cli, err := client.NewEnvClient()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -45,15 +45,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 +64,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 +78,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)
|
||||
}
|
||||
|
||||
@@ -107,32 +110,40 @@ func goldenPath(t *testing.T, metric bool) {
|
||||
}
|
||||
|
||||
// TestSecurityVulnerabilities checks for any vulnerabilities in the image, as reported
|
||||
// by Ubuntu
|
||||
// by Red Hat
|
||||
func TestSecurityVulnerabilities(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cli, err := client.NewEnvClient()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
rc, _ := runContainerOneShot(t, cli, "bash", "-c", "test -d /etc/apt")
|
||||
rc, _ := runContainerOneShot(t, cli, "bash", "-c", "command -v microdnf && test -e /etc/yum.repos.d/ubi.repo")
|
||||
if rc != 0 {
|
||||
t.Skip("Skipping test because container is not Ubuntu-based")
|
||||
}
|
||||
// Override the entrypoint to make "apt" only receive security updates, then check for updates
|
||||
var url string
|
||||
if runtime.GOARCH == "amd64" {
|
||||
url = "http://security.ubuntu.com/ubuntu/"
|
||||
} else {
|
||||
url = "http://ports.ubuntu.com/ubuntu-ports/"
|
||||
}
|
||||
rc, log := runContainerOneShot(t, cli, "bash", "-c", "source /etc/os-release && echo \"deb "+url+" ${VERSION_CODENAME}-security main restricted\" > /etc/apt/sources.list && apt-get update 2>&1 >/dev/null && apt-get --simulate -qq upgrade")
|
||||
if rc != 0 {
|
||||
t.Fatalf("Expected success, got %v", rc)
|
||||
}
|
||||
lines := strings.Split(strings.TrimSpace(log), "\n")
|
||||
if len(lines) > 0 && lines[0] != "" {
|
||||
t.Errorf("Expected no vulnerabilities, found the following:\n%v", log)
|
||||
t.Skip("Skipping test because container is based on ubi-minimal, which doesn't include yum")
|
||||
}
|
||||
// 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) {
|
||||
@@ -155,11 +166,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 +180,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 +188,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)
|
||||
}
|
||||
|
||||
@@ -183,7 +198,7 @@ func withVolume(t *testing.T, metric bool) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
vol := createVolume(t, cli)
|
||||
vol := createVolume(t, cli, t.Name())
|
||||
defer removeVolume(t, cli, vol.Name)
|
||||
containerConfig := container.Config{
|
||||
Image: imageName(),
|
||||
@@ -221,10 +236,67 @@ func withVolume(t *testing.T, metric bool) {
|
||||
waitForReady(t, cli, ctr2.ID)
|
||||
}
|
||||
|
||||
// TestWithSplitVolumesLogsData starts a queue manager with separate log/data mounts
|
||||
func TestWithSplitVolumesLogsData(t *testing.T) {
|
||||
cli, err := client.NewEnvClient()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
qmsharedlogs := createVolume(t, cli, "qmsharedlogs")
|
||||
defer removeVolume(t, cli, qmsharedlogs.Name)
|
||||
qmshareddata := createVolume(t, cli, "qmshareddata")
|
||||
defer removeVolume(t, cli, qmshareddata.Name)
|
||||
|
||||
err, qmID, qmVol := startMultiVolumeQueueManager(t, cli, true, qmsharedlogs.Name, qmshareddata.Name, []string{"LICENSE=accept", "MQ_QMGR_NAME=qm1"})
|
||||
|
||||
defer removeVolume(t, cli, qmVol)
|
||||
defer cleanContainer(t, cli, qmID)
|
||||
|
||||
waitForReady(t, cli, qmID)
|
||||
}
|
||||
|
||||
// TestWithSplitVolumesLogsOnly starts a queue manager with a separate log mount
|
||||
func TestWithSplitVolumesLogsOnly(t *testing.T) {
|
||||
cli, err := client.NewEnvClient()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
qmsharedlogs := createVolume(t, cli, "qmsharedlogs")
|
||||
defer removeVolume(t, cli, qmsharedlogs.Name)
|
||||
|
||||
err, qmID, qmVol := startMultiVolumeQueueManager(t, cli, true, qmsharedlogs.Name, "", []string{"LICENSE=accept", "MQ_QMGR_NAME=qm1"})
|
||||
|
||||
defer removeVolume(t, cli, qmVol)
|
||||
defer cleanContainer(t, cli, qmID)
|
||||
|
||||
waitForReady(t, cli, qmID)
|
||||
}
|
||||
|
||||
// TestWithSplitVolumesDataOnly starts a queue manager with a separate data mount
|
||||
func TestWithSplitVolumesDataOnly(t *testing.T) {
|
||||
cli, err := client.NewEnvClient()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
qmshareddata := createVolume(t, cli, "qmshareddata")
|
||||
defer removeVolume(t, cli, qmshareddata.Name)
|
||||
|
||||
err, qmID, qmVol := startMultiVolumeQueueManager(t, cli, true, "", qmshareddata.Name, []string{"LICENSE=accept", "MQ_QMGR_NAME=qm1"})
|
||||
|
||||
defer removeVolume(t, cli, qmVol)
|
||||
defer cleanContainer(t, cli, qmID)
|
||||
|
||||
waitForReady(t, cli, qmID)
|
||||
}
|
||||
|
||||
// TestNoVolumeWithRestart ensures a queue manager container can be stopped
|
||||
// and restarted cleanly
|
||||
func TestNoVolumeWithRestart(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cli, err := client.NewEnvClient()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -240,58 +312,138 @@ 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, t.Name())
|
||||
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,11 +452,12 @@ 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)
|
||||
}
|
||||
vol := createVolume(t, cli)
|
||||
vol := createVolume(t, cli, t.Name())
|
||||
defer removeVolume(t, cli, vol.Name)
|
||||
containerConfig := container.Config{
|
||||
Image: imageName(),
|
||||
@@ -348,6 +501,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 +538,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 +546,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 +572,244 @@ 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))
|
||||
}
|
||||
}
|
||||
|
||||
// TestRedactValidMQSC creates a new image with a Valid 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 TestRedactValidMQSC(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cli, err := client.NewEnvClient()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
passwords := "hippoman4567"
|
||||
sslcryp := fmt.Sprintf("GSK_PKCS11=/usr/lib/pkcs11/PKCS11_API.so;token-label;%s;SYMMETRIC_CIPHER_ON;", passwords)
|
||||
|
||||
/* LDAPPWD*/
|
||||
fmt.Fprintf(&buf, "DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) CONNAME('test(24)') SHORTUSR('sn') LDAPUSER('user') LDAPPWD('%v')\n", passwords)
|
||||
fmt.Fprintf(&buf, "ALTER AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) ldappwd('%v')\n", passwords)
|
||||
fmt.Fprintf(&buf, "ALTER AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) lDaPpWd('%v')\n", passwords)
|
||||
fmt.Fprintf(&buf, "ALTER AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD \t('%v')\n", passwords)
|
||||
fmt.Fprintf(&buf, "ALTER AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) +\n LDAP+\n PWD('%v')\n", passwords)
|
||||
fmt.Fprintf(&buf, "ALTER AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) -\nLDAPP-\nWD('%v')\n", passwords)
|
||||
fmt.Fprintf(&buf, "ALTER AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) +\n*test comment\n LDAPP-\n*test comment2\nWD('%v')\n", passwords)
|
||||
fmt.Fprintf(&buf, "ALTER AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD(%v)\n", passwords)
|
||||
|
||||
/* PASSWORD */
|
||||
fmt.Fprintf(&buf, "DEFINE CHANNEL(TEST2) CHLTYPE(SDR) CONNAME('test(24)') XMITQ('fake') PASSWORD('%v')\n", passwords)
|
||||
fmt.Fprintf(&buf, "ALTER CHANNEL(TEST2) CHLTYPE(SDR) password('%v')\n", passwords)
|
||||
fmt.Fprintf(&buf, "ALTER CHANNEL(TEST2) CHLTYPE(SDR) pAsSwOrD('%v')\n", passwords)
|
||||
fmt.Fprintf(&buf, "ALTER CHANNEL(TEST2) CHLTYPE(SDR) PASSWORD \t('%v')\n", passwords)
|
||||
fmt.Fprintf(&buf, "ALTER CHANNEL(TEST2) +\n CHLTYPE(SDR) PASS+\n WORD+\n ('%v')\n", passwords)
|
||||
fmt.Fprintf(&buf, "ALTER CHANNEL(TEST2) -\nCHLTYPE(SDR) PASS-\nWORD-\n('%v')\n", passwords)
|
||||
fmt.Fprintf(&buf, "ALTER CHANNEL(TEST2) +\n CHLTYPE(SDR) PASS-\n*comemnt 2\nWORD+\n*test comment\n ('%v')\n", passwords)
|
||||
fmt.Fprintf(&buf, "ALTER CHANNEL(TEST2) CHLTYPE(SDR) PASSWORD(%s)\n", passwords)
|
||||
|
||||
/* SSLCRYP */
|
||||
fmt.Fprintf(&buf, "ALTER QMGR SSLCRYP('%v')\n", sslcryp)
|
||||
fmt.Fprintf(&buf, "ALTER QMGR sslcryp('%v')\n", sslcryp)
|
||||
fmt.Fprintf(&buf, "ALTER QMGR SsLcRyP('%v')\n", sslcryp)
|
||||
fmt.Fprintf(&buf, "ALTER QMGR SSLCRYP \t('%v')\n", sslcryp)
|
||||
fmt.Fprintf(&buf, "ALTER QMGR +\n SSL+\n CRYP+\n ('%v')\n", sslcryp)
|
||||
fmt.Fprintf(&buf, "ALTER QMGR -\nSSLC-\nRYP-\n('%v')\n", sslcryp)
|
||||
fmt.Fprintf(&buf, "ALTER QMGR +\n*commenttime\n SSL-\n*commentagain\nCRYP+\n*last comment\n ('%v')\n", 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)))
|
||||
for scanner.Scan() {
|
||||
s := scanner.Text()
|
||||
if strings.Contains(s, sslcryp) || strings.Contains(s, passwords) {
|
||||
t.Fatalf("Expected redacted MQSC output, got: %v", s)
|
||||
}
|
||||
}
|
||||
err = scanner.Err()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestRedactValidMQSC creates a new image with a Invalid 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 TestRedactInvalidMQSC(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cli, err := client.NewEnvClient()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
passwords := "hippoman4567"
|
||||
sslcryp := fmt.Sprintf("GSK_PKCS11=/usr/lib/pkcs11/PKCS11_API.so;token-label;%s;SYMMETRIC_CIPHER_ON;", passwords)
|
||||
|
||||
/* LDAPPWD*/
|
||||
fmt.Fprintf(&buf, "DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) CONNAME('test(24)') SHORTUSR('sn') LDAPUSER('user') LDAPPWD('%v')\n", passwords)
|
||||
fmt.Fprintf(&buf, "ALTER AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPPPPPP('%v')\n", passwords)
|
||||
fmt.Fprintf(&buf, "ALTER AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD['%v']\n", passwords)
|
||||
fmt.Fprintf(&buf, "ALTER AUTHINFO(TEST) AUTHTYPE(ARGHHH) LDAPPWD('%v')\n", passwords)
|
||||
fmt.Fprintf(&buf, "ALTER AUTHINFO(TEST) LDAPPWD('%v')\n", passwords)
|
||||
fmt.Fprintf(&buf, "ALTER AUTHINFO(TEST) ARGHAHA(IDPWLDAP) LDAPPWD('%v')\n", passwords)
|
||||
fmt.Fprintf(&buf, "ALTER AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD '%v'\n", passwords)
|
||||
fmt.Fprintf(&buf, "ALTER AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD('%v') badvalues\n", passwords)
|
||||
fmt.Fprintf(&buf, "ALTER AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) badvales LDAPPWD('%v')\n", passwords)
|
||||
fmt.Fprintf(&buf, "ALTER AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD{'%v'}\n", passwords)
|
||||
fmt.Fprintf(&buf, "ALTER AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD<'%v'>\n", passwords)
|
||||
fmt.Fprintf(&buf, "ALTER AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD('%v'+\n p['il6])\n", passwords)
|
||||
fmt.Fprintf(&buf, "ALTER AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD('%v'/653***)\n", passwords)
|
||||
fmt.Fprintf(&buf, "ALTER AUTHINFO(TEST) LDAPPWD('%v'\n DISPLAY QMGR", passwords)
|
||||
fmt.Fprintf(&buf, "ALTER AUTHINFO(TEST) LDAPPWD('%v💩')\n", passwords)
|
||||
fmt.Fprintf(&buf, "ALTER AUTHINFO(TEST) LDAPPWD💩('%v')\n", passwords)
|
||||
fmt.Fprintf(&buf, "ALTER AUTHINFO(TEST) LDAP+\n 💩PWD('%v')\n", passwords)
|
||||
fmt.Fprintf(&buf, "ALTER AUTHINFO(TEST) 💩 LDAPPWD('%v')\n", passwords)
|
||||
fmt.Fprintf(&buf, "ALTER AUTHINFO(TEST) LDAPPWD 💩 ('%v')\n", passwords)
|
||||
fmt.Fprintf(&buf, "ALTER AUTHINFO(TEST) LDAPPWD('%v') 💩\n", passwords)
|
||||
fmt.Fprintf(&buf, "ALTER 💩 AUTHINFO(TEST) LDAPPWD('%v')\n", passwords)
|
||||
|
||||
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)
|
||||
rc := waitForContainer(t, cli, id, 20*time.Second)
|
||||
if rc != 1 {
|
||||
t.Errorf("Expected rc=1, got rc=%v", rc)
|
||||
}
|
||||
scanner := bufio.NewScanner(strings.NewReader(inspectLogs(t, cli, id)))
|
||||
for scanner.Scan() {
|
||||
s := scanner.Text()
|
||||
if strings.Contains(s, sslcryp) || strings.Contains(s, passwords) {
|
||||
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 +822,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 +864,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 +899,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 +942,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 +990,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 +1003,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
|
||||
|
||||
@@ -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) {
|
||||
@@ -281,6 +398,14 @@ func stopContainer(t *testing.T, cli *client.Client, ID string) {
|
||||
}
|
||||
}
|
||||
|
||||
func killContainer(t *testing.T, cli *client.Client, ID string, signal string) {
|
||||
t.Logf("Killing container: %v", ID)
|
||||
err := cli.ContainerKill(context.Background(), ID, signal)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func getExitCodeFilename(t *testing.T) string {
|
||||
return t.Name() + "ExitCode"
|
||||
}
|
||||
@@ -309,19 +434,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 +520,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 {
|
||||
@@ -405,6 +530,9 @@ func waitForReady(t *testing.T, cli *client.Client, ID string) {
|
||||
if rc == 0 {
|
||||
t.Log("MQ is ready")
|
||||
return
|
||||
} else if rc == 10 {
|
||||
t.Log("MQ Readiness: Queue Manager Running as Standby")
|
||||
return
|
||||
}
|
||||
case <-ctx.Done():
|
||||
t.Fatal("Timed out waiting for container to become ready")
|
||||
@@ -440,17 +568,17 @@ func removeNetwork(t *testing.T, cli *client.Client, ID string) {
|
||||
}
|
||||
}
|
||||
|
||||
func createVolume(t *testing.T, cli *client.Client) types.Volume {
|
||||
func createVolume(t *testing.T, cli *client.Client, name string) types.Volume {
|
||||
v, err := cli.VolumeCreate(context.Background(), volume.VolumesCreateBody{
|
||||
Driver: "local",
|
||||
DriverOpts: map[string]string{},
|
||||
Labels: map[string]string{},
|
||||
Name: t.Name(),
|
||||
Name: name,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Logf("Created volume %v", t.Name())
|
||||
t.Logf("Created volume %v", v.Name)
|
||||
return v
|
||||
}
|
||||
|
||||
|
||||
234
test/docker/mq_multi_instance_test.go
Normal file
234
test/docker/mq_multi_instance_test.go
Normal file
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
© 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 main
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/client"
|
||||
)
|
||||
|
||||
var miEnv = []string{
|
||||
"LICENSE=accept",
|
||||
"MQ_QMGR_NAME=QM1",
|
||||
"MQ_MULTI_INSTANCE=true",
|
||||
}
|
||||
|
||||
// TestMultiInstanceStartStop creates 2 containers in a multi instance queue manager configuration
|
||||
// and starts/stop them checking we always have an active and standby
|
||||
func TestMultiInstanceStartStop(t *testing.T) {
|
||||
cli, err := client.NewEnvClient()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err, qm1aId, qm1bId, volumes := configureMultiInstance(t, cli)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, volume := range volumes {
|
||||
defer removeVolume(t, cli, volume)
|
||||
}
|
||||
defer cleanContainer(t, cli, qm1aId)
|
||||
defer cleanContainer(t, cli, qm1bId)
|
||||
|
||||
waitForReady(t, cli, qm1aId)
|
||||
waitForReady(t, cli, qm1bId)
|
||||
|
||||
err, active, standby := getActiveStandbyQueueManager(t, cli, qm1aId, qm1bId)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
killContainer(t, cli, active, "SIGTERM")
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
if status := getQueueManagerStatus(t, cli, standby, "QM1"); strings.Compare(status, "Running") != 0 {
|
||||
t.Fatalf("Expected QM1 to be running as active queue manager, dspmq returned status of %v", status)
|
||||
}
|
||||
|
||||
startContainer(t, cli, qm1aId)
|
||||
waitForReady(t, cli, qm1aId)
|
||||
|
||||
err, _, _ = getActiveStandbyQueueManager(t, cli, qm1aId, qm1bId)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// TestMultiInstanceContainerStop starts 2 containers in a multi instance queue manager configuration,
|
||||
// stops the active queue manager, then checks to ensure the backup queue manager becomes active
|
||||
func TestMultiInstanceContainerStop(t *testing.T) {
|
||||
cli, err := client.NewEnvClient()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err, qm1aId, qm1bId, volumes := configureMultiInstance(t, cli)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, volume := range volumes {
|
||||
defer removeVolume(t, cli, volume)
|
||||
}
|
||||
defer cleanContainer(t, cli, qm1aId)
|
||||
defer cleanContainer(t, cli, qm1bId)
|
||||
|
||||
waitForReady(t, cli, qm1aId)
|
||||
waitForReady(t, cli, qm1bId)
|
||||
|
||||
err, active, standby := getActiveStandbyQueueManager(t, cli, qm1aId, qm1bId)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
stopContainer(t, cli, active)
|
||||
|
||||
if status := getQueueManagerStatus(t, cli, standby, "QM1"); strings.Compare(status, "Running") != 0 {
|
||||
t.Fatalf("Expected QM1 to be running as active queue manager, dspmq returned status of %v", status)
|
||||
}
|
||||
}
|
||||
|
||||
// TestMultiInstanceRace starts 2 containers in separate goroutines in a multi instance queue manager
|
||||
// configuration, then checks to ensure that both an active and standby queue manager have been started
|
||||
func TestMultiInstanceRace(t *testing.T) {
|
||||
t.Skipf("Skipping %v until file lock is implemented", t.Name())
|
||||
|
||||
cli, err := client.NewEnvClient()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
qmsharedlogs := createVolume(t, cli, "qmsharedlogs")
|
||||
defer removeVolume(t, cli, qmsharedlogs.Name)
|
||||
qmshareddata := createVolume(t, cli, "qmshareddata")
|
||||
defer removeVolume(t, cli, qmshareddata.Name)
|
||||
|
||||
qmsChannel := make(chan QMChan)
|
||||
|
||||
go singleMultiInstanceQueueManager(t, cli, qmsharedlogs.Name, qmshareddata.Name, qmsChannel)
|
||||
go singleMultiInstanceQueueManager(t, cli, qmsharedlogs.Name, qmshareddata.Name, qmsChannel)
|
||||
|
||||
qm1a := <-qmsChannel
|
||||
if qm1a.Error != nil {
|
||||
t.Fatal(qm1a.Error)
|
||||
}
|
||||
|
||||
qm1b := <-qmsChannel
|
||||
if qm1b.Error != nil {
|
||||
t.Fatal(qm1b.Error)
|
||||
}
|
||||
|
||||
qm1aId, qm1aData := qm1a.QMId, qm1a.QMData
|
||||
qm1bId, qm1bData := qm1b.QMId, qm1b.QMData
|
||||
|
||||
defer removeVolume(t, cli, qm1aData)
|
||||
defer removeVolume(t, cli, qm1bData)
|
||||
defer cleanContainer(t, cli, qm1aId)
|
||||
defer cleanContainer(t, cli, qm1bId)
|
||||
|
||||
waitForReady(t, cli, qm1aId)
|
||||
waitForReady(t, cli, qm1bId)
|
||||
|
||||
err, _, _ = getActiveStandbyQueueManager(t, cli, qm1aId, qm1bId)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestMultiInstanceNoSharedMounts starts 2 multi instance queue managers without providing shared log/data
|
||||
// mounts, then checks to ensure that the container terminates with the expected message
|
||||
func TestMultiInstanceNoSharedMounts(t *testing.T) {
|
||||
t.Parallel()
|
||||
cli, err := client.NewEnvClient()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err, qm1aId, qm1aData := startMultiVolumeQueueManager(t, cli, true, "", "", miEnv)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
defer removeVolume(t, cli, qm1aData)
|
||||
defer cleanContainer(t, cli, qm1aId)
|
||||
|
||||
waitForTerminationMessage(t, cli, qm1aId, "Missing required mount '/mnt/mqm-log'", 30*time.Second)
|
||||
}
|
||||
|
||||
// TestMultiInstanceNoSharedLogs starts 2 multi instance queue managers without providing a shared log
|
||||
// mount, then checks to ensure that the container terminates with the expected message
|
||||
func TestMultiInstanceNoSharedLogs(t *testing.T) {
|
||||
cli, err := client.NewEnvClient()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
qmshareddata := createVolume(t, cli, "qmshareddata")
|
||||
defer removeVolume(t, cli, qmshareddata.Name)
|
||||
|
||||
err, qm1aId, qm1aData := startMultiVolumeQueueManager(t, cli, true, "", qmshareddata.Name, miEnv)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
defer removeVolume(t, cli, qm1aData)
|
||||
defer cleanContainer(t, cli, qm1aId)
|
||||
|
||||
waitForTerminationMessage(t, cli, qm1aId, "Missing required mount '/mnt/mqm-log'", 30*time.Second)
|
||||
}
|
||||
|
||||
// TestMultiInstanceNoSharedData starts 2 multi instance queue managers without providing a shared data
|
||||
// mount, then checks to ensure that the container terminates with the expected message
|
||||
func TestMultiInstanceNoSharedData(t *testing.T) {
|
||||
cli, err := client.NewEnvClient()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
qmsharedlogs := createVolume(t, cli, "qmsharedlogs")
|
||||
defer removeVolume(t, cli, qmsharedlogs.Name)
|
||||
|
||||
err, qm1aId, qm1aData := startMultiVolumeQueueManager(t, cli, true, qmsharedlogs.Name, "", miEnv)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
defer removeVolume(t, cli, qm1aData)
|
||||
defer cleanContainer(t, cli, qm1aId)
|
||||
|
||||
waitForTerminationMessage(t, cli, qm1aId, "Missing required mount '/mnt/mqm-data'", 30*time.Second)
|
||||
}
|
||||
|
||||
// TestMultiInstanceNoMounts starts 2 multi instance queue managers without providing a shared data
|
||||
// mount, then checks to ensure that the container terminates with the expected message
|
||||
func TestMultiInstanceNoMounts(t *testing.T) {
|
||||
cli, err := client.NewEnvClient()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err, qm1aId, qm1aData := startMultiVolumeQueueManager(t, cli, false, "", "", miEnv)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
defer removeVolume(t, cli, qm1aData)
|
||||
defer cleanContainer(t, cli, qm1aId)
|
||||
|
||||
waitForTerminationMessage(t, cli, qm1aId, "Missing required mount '/mnt/mqm'", 30*time.Second)
|
||||
}
|
||||
178
test/docker/mq_multi_instance_test_util.go
Normal file
178
test/docker/mq_multi_instance_test_util.go
Normal file
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
© 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 main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/network"
|
||||
"github.com/docker/docker/client"
|
||||
)
|
||||
|
||||
type QMChan struct {
|
||||
QMId string
|
||||
QMData string
|
||||
Error error
|
||||
}
|
||||
|
||||
// configureMultiInstance creates the volumes and containers required for basic testing
|
||||
// of multi instance queue managers. Returns error, qm1a ID, qm1b ID, slice of volume names
|
||||
func configureMultiInstance(t *testing.T, cli *client.Client) (error, string, string, []string) {
|
||||
|
||||
qmsharedlogs := createVolume(t, cli, "qmsharedlogs")
|
||||
qmshareddata := createVolume(t, cli, "qmshareddata")
|
||||
|
||||
err, qm1aId, qm1aData := startMultiVolumeQueueManager(t, cli, true, qmsharedlogs.Name, qmshareddata.Name, miEnv)
|
||||
if err != nil {
|
||||
return err, "", "", []string{}
|
||||
}
|
||||
time.Sleep(10 * time.Second)
|
||||
err, qm1bId, qm1bData := startMultiVolumeQueueManager(t, cli, true, qmsharedlogs.Name, qmshareddata.Name, miEnv)
|
||||
if err != nil {
|
||||
return err, "", "", []string{}
|
||||
}
|
||||
|
||||
volumes := []string{qmsharedlogs.Name, qmshareddata.Name, qm1aData, qm1bData}
|
||||
|
||||
return nil, qm1aId, qm1bId, volumes
|
||||
}
|
||||
|
||||
func singleMultiInstanceQueueManager(t *testing.T, cli *client.Client, qmsharedlogs string, qmshareddata string, qmsChannel chan QMChan) {
|
||||
err, qmId, qmData := startMultiVolumeQueueManager(t, cli, true, qmsharedlogs, qmshareddata, miEnv)
|
||||
if err != nil {
|
||||
qmsChannel <- QMChan{Error: err}
|
||||
}
|
||||
qmsChannel <- QMChan{QMId: qmId, QMData: qmData}
|
||||
}
|
||||
|
||||
func getHostConfig(t *testing.T, mounts int, qmsharedlogs string, qmshareddata string, qmdata string) container.HostConfig {
|
||||
|
||||
var hostConfig container.HostConfig
|
||||
|
||||
switch mounts {
|
||||
case 1:
|
||||
hostConfig = container.HostConfig{
|
||||
Binds: []string{
|
||||
coverageBind(t),
|
||||
qmdata + ":/mnt/mqm",
|
||||
},
|
||||
}
|
||||
case 2:
|
||||
hostConfig = container.HostConfig{
|
||||
Binds: []string{
|
||||
coverageBind(t),
|
||||
qmdata + ":/mnt/mqm",
|
||||
qmshareddata + ":/mnt/mqm-data",
|
||||
},
|
||||
}
|
||||
case 3:
|
||||
hostConfig = container.HostConfig{
|
||||
Binds: []string{
|
||||
coverageBind(t),
|
||||
qmdata + ":/mnt/mqm",
|
||||
qmsharedlogs + ":/mnt/mqm-log",
|
||||
},
|
||||
}
|
||||
case 4:
|
||||
hostConfig = container.HostConfig{
|
||||
Binds: []string{
|
||||
coverageBind(t),
|
||||
qmdata + ":/mnt/mqm",
|
||||
qmsharedlogs + ":/mnt/mqm-log",
|
||||
qmshareddata + ":/mnt/mqm-data",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return hostConfig
|
||||
}
|
||||
|
||||
func startMultiVolumeQueueManager(t *testing.T, cli *client.Client, dataVol bool, qmsharedlogs string, qmshareddata string, env []string) (error, string, string) {
|
||||
id := strconv.FormatInt(time.Now().UnixNano(), 10)
|
||||
qmdata := createVolume(t, cli, id)
|
||||
containerConfig := container.Config{
|
||||
Image: imageName(),
|
||||
Env: env,
|
||||
}
|
||||
var hostConfig container.HostConfig
|
||||
|
||||
if !dataVol {
|
||||
hostConfig = container.HostConfig{}
|
||||
} else if qmsharedlogs == "" && qmshareddata == "" {
|
||||
hostConfig = getHostConfig(t, 1, "", "", qmdata.Name)
|
||||
} else if qmsharedlogs == "" {
|
||||
hostConfig = getHostConfig(t, 2, "", qmshareddata, qmdata.Name)
|
||||
} else if qmshareddata == "" {
|
||||
hostConfig = getHostConfig(t, 3, qmsharedlogs, "", qmdata.Name)
|
||||
} else {
|
||||
hostConfig = getHostConfig(t, 4, qmsharedlogs, qmshareddata, qmdata.Name)
|
||||
}
|
||||
networkingConfig := network.NetworkingConfig{}
|
||||
qm, err := cli.ContainerCreate(context.Background(), &containerConfig, &hostConfig, &networkingConfig, t.Name()+id)
|
||||
if err != nil {
|
||||
return err, "", ""
|
||||
}
|
||||
startContainer(t, cli, qm.ID)
|
||||
|
||||
return nil, qm.ID, qmdata.Name
|
||||
}
|
||||
|
||||
func getActiveStandbyQueueManager(t *testing.T, cli *client.Client, qm1aId string, qm1bId string) (error, string, string) {
|
||||
qm1aStatus := getQueueManagerStatus(t, cli, qm1aId, "QM1")
|
||||
qm1bStatus := getQueueManagerStatus(t, cli, qm1bId, "QM1")
|
||||
|
||||
if qm1aStatus == "Running" && qm1bStatus == "Running as standby" {
|
||||
return nil, qm1aId, qm1bId
|
||||
} else if qm1bStatus == "Running" && qm1aStatus == "Running as standby" {
|
||||
return nil, qm1bId, qm1aId
|
||||
}
|
||||
err := fmt.Errorf("Expected to be running in multi instance configuration, got status 1) %v status 2) %v", qm1aStatus, qm1bStatus)
|
||||
return err, "", ""
|
||||
}
|
||||
|
||||
func getQueueManagerStatus(t *testing.T, cli *client.Client, containerID string, queueManagerName string) string {
|
||||
_, dspmqOut := execContainer(t, cli, containerID, "mqm", []string{"bash", "-c", "dspmq", "-m", queueManagerName})
|
||||
regex := regexp.MustCompile(`STATUS\(.*\)`)
|
||||
status := regex.FindString(dspmqOut)
|
||||
status = strings.TrimSuffix(strings.TrimPrefix(status, "STATUS("), ")")
|
||||
return status
|
||||
}
|
||||
|
||||
func waitForTerminationMessage(t *testing.T, cli *client.Client, qmId string, terminationString string, timeout time.Duration) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||
defer cancel()
|
||||
for {
|
||||
select {
|
||||
case <-time.After(1 * time.Second):
|
||||
m := terminationMessage(t, cli, qmId)
|
||||
if m != "" {
|
||||
if !strings.Contains(m, terminationString) {
|
||||
t.Fatalf("Expected container to fail on missing required mount. Got termination message: %v", m)
|
||||
}
|
||||
return
|
||||
}
|
||||
case <-ctx.Done():
|
||||
t.Fatal("Timed out waiting for container to terminate")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
@@ -15,7 +15,7 @@
|
||||
###############################################################################
|
||||
# Application build environment (Maven)
|
||||
###############################################################################
|
||||
FROM maven:3-ibmjava as builder
|
||||
FROM docker.io/maven:3-ibmjava as builder
|
||||
COPY pom.xml /usr/src/mymaven/
|
||||
WORKDIR /usr/src/mymaven
|
||||
# Download dependencies separately, so Docker caches them
|
||||
@@ -30,7 +30,7 @@ RUN find /usr/src/mymaven
|
||||
###############################################################################
|
||||
# Application runtime (JRE only, no build environment)
|
||||
###############################################################################
|
||||
FROM ibmjava:8-jre
|
||||
FROM docker.io/ibmjava:8-jre
|
||||
COPY --from=builder /usr/src/mymaven/target/*.jar /opt/app/
|
||||
COPY --from=builder /usr/src/mymaven/target/lib/*.jar /opt/app/
|
||||
ENTRYPOINT ["java", "-classpath", "/opt/app/*", "org.junit.platform.console.ConsoleLauncher", "-p", "com.ibm.mqcontainer.test", "--details", "verbose"]
|
||||
|
||||
@@ -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
|
||||
@@ -67,8 +73,9 @@ rm -rf ${mnt_mq}/var/cache/yum/*
|
||||
|
||||
buildah config \
|
||||
--os linux \
|
||||
--label architecture=x86_64 \
|
||||
--label architecture=amd64 \
|
||||
--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
|
||||
|
||||
@@ -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.
@@ -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 \
|
||||
|
||||
@@ -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-----
|
||||
|
||||
@@ -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.
10
vendor/software.sslmate.com/src/go-pkcs12/.gitattributes
generated
vendored
Normal file
10
vendor/software.sslmate.com/src/go-pkcs12/.gitattributes
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
# Treat all files in this repo as binary, with no git magic updating
|
||||
# line endings. Windows users contributing to Go will need to use a
|
||||
# modern version of git and editors capable of LF line endings.
|
||||
#
|
||||
# We'll prevent accidental CRLF line endings from entering the repo
|
||||
# via the git-review gofmt checks.
|
||||
#
|
||||
# See golang.org/issue/9281
|
||||
|
||||
* -text
|
||||
2
vendor/software.sslmate.com/src/go-pkcs12/.gitignore
generated
vendored
Normal file
2
vendor/software.sslmate.com/src/go-pkcs12/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
# Add no patterns to .hgignore except for files generated by the build.
|
||||
last-change
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user