Compare commits
45 Commits
9.3.1
...
dependabot
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a7a5e83383 | ||
|
|
53790e17c1 | ||
|
|
319c120625 | ||
|
|
8d7adc8581 | ||
|
|
60f7225442 | ||
|
|
38c55eae86 | ||
|
|
6084af2386 | ||
|
|
d2f7d3764e | ||
|
|
b47ad7fde2 | ||
|
|
167ec03f04 | ||
|
|
98129eb660 | ||
|
|
9c7f49d8d3 | ||
|
|
572e883841 | ||
|
|
26195d1bd9 | ||
|
|
c8d13e36e6 | ||
|
|
ca719539a7 | ||
|
|
b281d59577 | ||
|
|
5f0142acdc | ||
|
|
4588cd44f9 | ||
|
|
5c4422badf | ||
|
|
f2842d7eee | ||
|
|
4e9877722a | ||
|
|
8efaa55c4f | ||
|
|
537320a32d | ||
|
|
ed618dc6f6 | ||
|
|
862427306b | ||
|
|
e08a8121f1 | ||
|
|
7c4598bd87 | ||
|
|
d3a197e0f2 | ||
|
|
9518a6d3ed | ||
|
|
53af11ff0d | ||
|
|
88f72409e4 | ||
|
|
ecb71fcca7 | ||
|
|
794d1ed2b2 | ||
|
|
1ead807326 | ||
|
|
046517af63 | ||
|
|
13b4e0772d | ||
|
|
4af8458ba5 | ||
|
|
7ecb80540e | ||
|
|
00648afa87 | ||
|
|
98a1939577 | ||
|
|
0ddccb971d | ||
|
|
50260e1f57 | ||
|
|
a824b75bfa | ||
|
|
336d542ff1 |
@@ -1,2 +0,0 @@
|
||||
downloads
|
||||
.git
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,4 @@
|
||||
.dockerignore
|
||||
.DS_Store
|
||||
.vscode
|
||||
test/docker/coverage
|
||||
|
||||
32
.travis.yml
32
.travis.yml
@@ -1,4 +1,4 @@
|
||||
# © Copyright IBM Corporation 2018, 2020
|
||||
# © Copyright IBM Corporation 2018, 2023
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -18,14 +18,14 @@ sudo: required
|
||||
language: go
|
||||
|
||||
go:
|
||||
- "1.17.12"
|
||||
- "1.18.9"
|
||||
|
||||
services:
|
||||
- docker
|
||||
|
||||
env:
|
||||
global:
|
||||
- MAIN_BRANCH=v9.3.1
|
||||
- MAIN_BRANCH=private-master
|
||||
- TAGCACHE_FILE=tagcache
|
||||
- RELEASE=r1
|
||||
|
||||
@@ -38,51 +38,51 @@ go_import_path: "github.com/ibm-messaging/mq-container"
|
||||
jobs:
|
||||
include:
|
||||
- stage: basic-build
|
||||
if: branch != v9.3.1 AND tag IS blank
|
||||
if: branch != private-master AND tag IS blank
|
||||
name: "Basic AMD64 build"
|
||||
os: linux
|
||||
env:
|
||||
- MQ_ARCHIVE_REPOSITORY_DEV=$MQ_931_ARCHIVE_REPOSITORY_DEV_AMD64
|
||||
- MQ_ARCHIVE_REPOSITORY_DEV=$MQ_932_ARCHIVE_REPOSITORY_DEV_AMD64
|
||||
script: bash -e travis-build-scripts/run.sh
|
||||
|
||||
# CD Build
|
||||
|
||||
- stage: global-tag
|
||||
if: branch = v9.3.1 AND type != pull_request OR tag =~ ^release-candidate*
|
||||
if: branch = private-master AND type != pull_request OR tag =~ ^release-candidate*
|
||||
name: "Generate Global Tag"
|
||||
os: linux
|
||||
script: bash -e travis-build-scripts/global-tag.sh
|
||||
- stage: build
|
||||
if: branch = v9.3.1 OR tag =~ ^release-candidate*
|
||||
if: branch = private-master OR tag =~ ^release-candidate*
|
||||
name: "Multi-Arch AMD64 build"
|
||||
os: linux
|
||||
env:
|
||||
- BUILD_ALL=true
|
||||
- MQ_ARCHIVE_REPOSITORY=$MQ_931_ARCHIVE_REPOSITORY_AMD64
|
||||
- MQ_ARCHIVE_REPOSITORY_DEV=$MQ_931_ARCHIVE_REPOSITORY_DEV_AMD64
|
||||
- MQ_ARCHIVE_REPOSITORY=$MQ_932_ARCHIVE_REPOSITORY_AMD64
|
||||
- MQ_ARCHIVE_REPOSITORY_DEV=$MQ_932_ARCHIVE_REPOSITORY_DEV_AMD64
|
||||
script: bash -e travis-build-scripts/run.sh
|
||||
- stage: build
|
||||
if: branch = v9.3.1 OR tag =~ ^release-candidate*
|
||||
if: branch = private-master OR tag =~ ^release-candidate*
|
||||
name: "Multi-Arch S390X build"
|
||||
os: linux-s390
|
||||
env:
|
||||
- BUILD_ALL=true
|
||||
- TEST_OPTS_DOCKER="-run TestGoldenPathWithMetrics"
|
||||
- MQ_ARCHIVE_REPOSITORY=$MQ_931_ARCHIVE_REPOSITORY_S390X
|
||||
- MQ_ARCHIVE_REPOSITORY_DEV=$MQ_931_ARCHIVE_REPOSITORY_DEV_S390X
|
||||
- MQ_ARCHIVE_REPOSITORY=$MQ_932_ARCHIVE_REPOSITORY_S390X
|
||||
- MQ_ARCHIVE_REPOSITORY_DEV=$MQ_932_ARCHIVE_REPOSITORY_DEV_S390X
|
||||
script: bash -e travis-build-scripts/run.sh
|
||||
- stage: build
|
||||
if: branch = v9.3.1 OR tag =~ ^release-candidate*
|
||||
if: branch = private-master OR tag =~ ^release-candidate*
|
||||
name: "Multi-Arch PPC64LE build"
|
||||
os: linux-ppc64le
|
||||
env:
|
||||
- BUILD_ALL=true
|
||||
- TEST_OPTS_DOCKER="-run TestGoldenPathWithMetrics"
|
||||
- MQ_ARCHIVE_REPOSITORY=$MQ_931_ARCHIVE_REPOSITORY_PPC64LE
|
||||
- MQ_ARCHIVE_REPOSITORY_DEV=$MQ_931_ARCHIVE_REPOSITORY_DEV_PPC64LE
|
||||
- MQ_ARCHIVE_REPOSITORY=$MQ_932_ARCHIVE_REPOSITORY_PPC64LE
|
||||
- MQ_ARCHIVE_REPOSITORY_DEV=$MQ_932_ARCHIVE_REPOSITORY_DEV_PPC64LE
|
||||
script: bash -e travis-build-scripts/run.sh
|
||||
- stage: push-manifest
|
||||
if: branch = v9.3.1 AND type != pull_request OR tag =~ ^release-candidate*
|
||||
if: branch = private-master AND type != pull_request OR tag =~ ^release-candidate*
|
||||
name: "Push Manifest-list to registry"
|
||||
env:
|
||||
- PUSH_MANIFEST_ONLY=true
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"settingsInheritedFrom": "whitesource-config/whitesource-config@master",
|
||||
"scanSettings": {
|
||||
"configMode": "LOCAL",
|
||||
"baseBranches": ["private-master", "v9.2.0.x-eus", "v9.3.0.x"]
|
||||
},
|
||||
"issueSettings": {
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
# Change log
|
||||
|
||||
## 9.3.1.1 (2023-01)
|
||||
## 9.3.2.0 (2023-02)
|
||||
|
||||
* Updated to MQ version 9.3.1.1
|
||||
* Updated to MQ version 9.3.2.0
|
||||
* Queue manager certificates with the same Subject Distinguished Name (DN) as the issuer (CA) certificate are not supported. A certificate must have a unique Subject Distinguished Name.
|
||||
* New logging environment variables: MQ_LOGGING_CONSOLE_SOURCE, MQ_LOGGING_CONSOLE_FORMAT, MQ_LOGGING_CONSOLE_EXCLUDE_ID. The LOG_FORMAT variable is deprecated.
|
||||
* New environment variable: MQ_QMGR_LOG_FILE_PAGES
|
||||
|
||||
## 9.3.1.0-r2 (2022-11)
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# © Copyright IBM Corporation 2015, 2022
|
||||
# © Copyright IBM Corporation 2015, 2023
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -13,37 +13,32 @@
|
||||
# limitations under the License.
|
||||
|
||||
ARG BASE_IMAGE=registry.access.redhat.com/ubi8/ubi-minimal
|
||||
ARG BASE_TAG=8.7-1031
|
||||
ARG BASE_TAG=8.7-1049.1675784874
|
||||
ARG BUILDER_IMAGE=registry.access.redhat.com/ubi8/go-toolset
|
||||
ARG BUILDER_TAG=1.17.12-11
|
||||
ARG BUILDER_TAG=1.18.9-8.1675807488
|
||||
ARG GO_WORKDIR=/opt/app-root/src/go/src/github.com/ibm-messaging/mq-container
|
||||
ARG MQ_URL="https://public.dhe.ibm.com/ibmdl/export/pub/software/websphere/messaging/mqadv/9.3.1.1-IBM-MQ-Advanced-for-Developers-Non-Install-LinuxX64.tar.gz"
|
||||
ARG MQ_ARCHIVE="downloads/9.3.2.0-IBM-MQ-Advanced-for-Developers-Non-Install-LinuxX64.tar.gz"
|
||||
|
||||
###############################################################################
|
||||
# Build stage to build Go code
|
||||
###############################################################################
|
||||
FROM $BUILDER_IMAGE:$BUILDER_TAG as builder
|
||||
# The URL to download the MQ installer from in tar.gz format
|
||||
# This assumes an archive containing the MQ Non-Install packages
|
||||
ARG MQ_URL
|
||||
ARG IMAGE_REVISION="Not specified"
|
||||
ARG IMAGE_SOURCE="Not specified"
|
||||
ARG IMAGE_TAG="Not specified"
|
||||
ARG GO_WORKDIR
|
||||
ARG MQ_ARCHIVE
|
||||
USER 0
|
||||
WORKDIR /opt/mqm
|
||||
# Download and extract MQ files, to get the MQ client needed to compile.
|
||||
# Only extract certain MQ files to make the build quicker
|
||||
RUN curl --fail --location $MQ_URL | tar --extract --gunzip \
|
||||
&& chown -R 1001:root /opt/mqm/*
|
||||
WORKDIR $GO_WORKDIR/
|
||||
ADD $MQ_ARCHIVE /opt/mqm
|
||||
ENV CGO_CFLAGS="-I/opt/mqm/inc/" \
|
||||
CGO_LDFLAGS_ALLOW="-Wl,-rpath.*" \
|
||||
PATH="${PATH}:/opt/mqm/bin"
|
||||
COPY go.mod go.sum ./
|
||||
COPY cmd/ ./cmd
|
||||
COPY internal/ ./internal
|
||||
COPY pkg/ ./pkg
|
||||
COPY vendor/ ./vendor
|
||||
ENV CGO_CFLAGS="-I/opt/mqm/inc/" \
|
||||
CGO_LDFLAGS_ALLOW="-Wl,-rpath.*" \
|
||||
PATH="${PATH}:/opt/mqm/bin"
|
||||
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/ \
|
||||
&& go build ./cmd/chkmqready/ \
|
||||
&& go build ./cmd/chkmqhealthy/ \
|
||||
@@ -58,11 +53,44 @@ RUN go build -ldflags "-X \"main.ImageCreated=$(date --iso-8601=seconds)\" -X \"
|
||||
&& go test -v ./internal/... \
|
||||
&& go vet ./cmd/... ./internal/...
|
||||
|
||||
###############################################################################
|
||||
# Build stage to reduce MQ packages included using genmqpkg
|
||||
###############################################################################
|
||||
FROM $BASE_IMAGE:$BASE_TAG AS mq-redux
|
||||
ARG BASE_IMAGE
|
||||
ARG BASE_TAG
|
||||
ARG MQ_ARCHIVE
|
||||
WORKDIR /tmp/mq
|
||||
ENV genmqpkg_inc32=0 \
|
||||
genmqpkg_incadm=1 \
|
||||
genmqpkg_incamqp=0 \
|
||||
genmqpkg_incams=1 \
|
||||
genmqpkg_inccbl=0 \
|
||||
genmqpkg_inccics=0 \
|
||||
genmqpkg_inccpp=0 \
|
||||
genmqpkg_incdnet=0 \
|
||||
genmqpkg_incjava=1 \
|
||||
genmqpkg_incjre=1 \
|
||||
genmqpkg_incman=0 \
|
||||
genmqpkg_incmqbc=0 \
|
||||
genmqpkg_incmqft=0 \
|
||||
genmqpkg_incmqsf=0 \
|
||||
genmqpkg_incmqxr=0 \
|
||||
genmqpkg_incnls=1 \
|
||||
genmqpkg_incras=1 \
|
||||
genmqpkg_incsamp=1 \
|
||||
genmqpkg_incsdk=0 \
|
||||
genmqpkg_inctls=1 \
|
||||
genmqpkg_incunthrd=0 \
|
||||
genmqpkg_incweb=1
|
||||
ADD $MQ_ARCHIVE /opt/mqm-noinstall
|
||||
# Run genmqpkg to reduce the MQ packages included
|
||||
RUN /opt/mqm-noinstall/bin/genmqpkg.sh -b /opt/mqm-redux
|
||||
|
||||
###############################################################################
|
||||
# Main build stage, to build MQ image
|
||||
###############################################################################
|
||||
FROM $BASE_IMAGE:$BASE_TAG AS mq-server
|
||||
# The MQ packages to install - see install-mq.sh for default value
|
||||
ARG MQ_URL
|
||||
ARG BASE_IMAGE
|
||||
ARG BASE_TAG
|
||||
@@ -79,15 +107,14 @@ LABEL summary="IBM MQ Advanced Server" \
|
||||
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" \
|
||||
base-image=$BASE_IMAGE \
|
||||
base-image-release=$BASE_TAG
|
||||
COPY install-mq.sh /usr/local/bin/
|
||||
COPY --from=mq-redux /opt/mqm-redux/ /opt/mqm/
|
||||
COPY setup-image.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 env \
|
||||
&& mkdir /opt/mqm \
|
||||
&& chmod u+x /usr/local/bin/install-*.sh \
|
||||
&& sleep 1 \
|
||||
&& chmod u+x /usr/local/bin/setup-image.sh \
|
||||
&& install-mq-server-prereqs.sh \
|
||||
&& install-mq.sh \
|
||||
&& setup-image.sh \
|
||||
&& /opt/mqm/bin/security/amqpamcf \
|
||||
&& chown -R 1001:root /opt/mqm/*
|
||||
COPY --from=builder $GO_WORKDIR/runmqserver /usr/local/bin/
|
||||
@@ -110,7 +137,9 @@ RUN chmod ug+x /usr/local/bin/runmqserver \
|
||||
EXPOSE 1414 9157 9443
|
||||
ENV MQ_OVERRIDE_DATA_PATH=/mnt/mqm/data MQ_OVERRIDE_INSTALLATION_NAME=Installation1 MQ_USER_NAME="mqm" PATH="${PATH}:/opt/mqm/bin"
|
||||
ENV MQ_GRACE_PERIOD=30
|
||||
ENV LANG=en_US.UTF-8 AMQ_DIAGNOSTIC_MSG_SEVERITY=1 AMQ_ADDITIONAL_JSON_LOG=1 LOG_FORMAT=basic
|
||||
ENV LANG=en_US.UTF-8 AMQ_DIAGNOSTIC_MSG_SEVERITY=1 AMQ_ADDITIONAL_JSON_LOG=1
|
||||
ENV MQ_LOGGING_CONSOLE_EXCLUDE_ID=AMQ5041I,AMQ5052I,AMQ5051I,AMQ5037I,AMQ5975I
|
||||
ENV WLP_LOGGING_MESSAGE_FORMAT=json
|
||||
# We can run as any UID
|
||||
USER 1001
|
||||
ENV MQ_CONNAUTH_USE_HTP=false
|
||||
@@ -121,9 +150,6 @@ ENTRYPOINT ["runmqserver"]
|
||||
###############################################################################
|
||||
# Use the Go toolset image, which already includes gcc and the MQ SDK
|
||||
FROM builder as cbuilder
|
||||
# The URL to download the MQ installer from in tar.gz format
|
||||
# This assumes an archive containing the MQ Non-Install packages
|
||||
ARG MQ_URL
|
||||
USER 0
|
||||
# Install the Apache Portable Runtime code (used for htpasswd hash checking)
|
||||
RUN yum --assumeyes --disableplugin=subscription-manager install apr-devel apr-util-openssl apr-util-devel
|
||||
|
||||
94
Makefile
94
Makefile
@@ -1,4 +1,4 @@
|
||||
# © Copyright IBM Corporation 2017, 2022
|
||||
# © Copyright IBM Corporation 2017, 2023
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -48,7 +48,7 @@ MQ_SDK_ARCHIVE ?= $(MQ_ARCHIVE_DEV_$(MQ_VERSION))
|
||||
# Options to `go test` for the Docker tests
|
||||
TEST_OPTS_DOCKER ?=
|
||||
# Timeout for the Docker tests
|
||||
TEST_TIMEOUT_DOCKER ?= 30m
|
||||
TEST_TIMEOUT_DOCKER ?= 45m
|
||||
# MQ_IMAGE_ADVANCEDSERVER is the name of the built MQ Advanced image
|
||||
MQ_IMAGE_ADVANCEDSERVER ?=ibm-mqadvanced-server
|
||||
# MQ_IMAGE_DEVSERVER is the name of the built MQ Advanced for Developers image
|
||||
@@ -75,8 +75,6 @@ VOLUME_MOUNT_OPTIONS ?= :Z
|
||||
###############################################################################
|
||||
# Other variables
|
||||
###############################################################################
|
||||
# Build doesn't work if BuildKit is enabled
|
||||
DOCKER_BUILDKIT=0
|
||||
# Lock Docker API version for compatibility with Podman and with the Docker version in Travis' Ubuntu Bionic
|
||||
DOCKER_API_VERSION=1.40
|
||||
GO_PKG_DIRS = ./cmd ./internal ./test
|
||||
@@ -340,14 +338,16 @@ test-advancedserver-cover: test/docker/vendor coverage
|
||||
# Command to build the image
|
||||
# Args: imageName, imageTag, dockerfile, extraArgs, dockerfileTarget
|
||||
# If the ARCH variable has been changed from the default value (arch_go variable), then the `--platform` parameter is added
|
||||
define build-mq-command
|
||||
# Args: imageName, imageTag, dockerfile, mqArchive, dockerfileTarget
|
||||
define build-mq
|
||||
rm -f .dockerignore && echo ".git\ndownloads\n!downloads/$4" > .dockerignore
|
||||
$(COMMAND) build \
|
||||
--tag $1:$2 \
|
||||
--file $3 \
|
||||
$4 \
|
||||
--build-arg IMAGE_REVISION="$(IMAGE_REVISION)" \
|
||||
--build-arg IMAGE_SOURCE="$(IMAGE_SOURCE)" \
|
||||
--build-arg IMAGE_TAG="$1:$2" \
|
||||
--build-arg MQ_ARCHIVE="downloads/$4" \
|
||||
--label version=$(MQ_VERSION) \
|
||||
--label name=$1 \
|
||||
--label build-date=$(shell date +%Y-%m-%dT%H:%M:%S%z) \
|
||||
@@ -362,57 +362,6 @@ define build-mq-command
|
||||
.
|
||||
endef
|
||||
|
||||
# Build using a separate container to host the MQ download files.
|
||||
# To minimize the layers in the resulting image, the download files can't be part of the build context.
|
||||
# The "docker build" command (and "podman build" on macOS) don't allow you to mount a directory into the build, so a
|
||||
# separate container is used to host a web server.
|
||||
# Note that for Podman, this means that you need to be using the "rootful" mode, because the rootless mode doesn't allow
|
||||
# much control of networking, so the containers can't talk to each other.
|
||||
define build-mq-using-web-server
|
||||
$(COMMAND) network create $(BUILD_SERVER_NETWORK)
|
||||
$(COMMAND) run \
|
||||
--rm \
|
||||
--name $(BUILD_SERVER_CONTAINER) \
|
||||
--network $(BUILD_SERVER_NETWORK) \
|
||||
--volume $(DOWNLOADS_DIR):/opt/app-root/src$(VOLUME_MOUNT_OPTIONS) \
|
||||
--detach \
|
||||
registry.access.redhat.com/ubi8/nginx-120 nginx -g "daemon off;" || ($(COMMAND) network rm $(BUILD_SERVER_NETWORK) && exit 1)
|
||||
BUILD_SERVER_IP=$$($(COMMAND) inspect -f '{{ .NetworkSettings.Networks.$(BUILD_SERVER_NETWORK).IPAddress }}' $(BUILD_SERVER_CONTAINER)); \
|
||||
$(call build-mq-command,$1,$2,$3,--network build --build-arg MQ_URL=http://$$BUILD_SERVER_IP:8080/$4,$5) || ($(COMMAND) rm -f $(BUILD_SERVER_CONTAINER) && $(COMMAND) network rm $(BUILD_SERVER_NETWORK) && exit 1)
|
||||
$(COMMAND) rm -f $(BUILD_SERVER_CONTAINER)
|
||||
$(COMMAND) network rm $(BUILD_SERVER_NETWORK)
|
||||
endef
|
||||
|
||||
# When building with Docker, always use the web server build because you can't use bind-mounted volumes.
|
||||
# Args: imageName, imageTag, dockerfile, mqArchive, dockerfileTarget
|
||||
define build-mq-docker
|
||||
$(call build-mq-using-web-server,$1,$2,$3,$4,$5)
|
||||
endef
|
||||
|
||||
# When building with Podman on macOS (Darwin), use the web server build because you can't use bind-mounted volumes with `podman build` on macOS
|
||||
# Args: imageName, imageTag, dockerfile, mqArchive, dockerfileTarget
|
||||
define build-mq-podman-Darwin
|
||||
$(call build-mq-using-web-server,$1,$2,$3,$4,$5)
|
||||
endef
|
||||
|
||||
# When building with Podman on Linux, just pass the downloads directory as a volume
|
||||
# Args: imageName, imageTag, dockerfile, mqArchive, dockerfileTarget
|
||||
define build-mq-podman-Linux
|
||||
$(call build-mq-command,$1,$2,$3,--volume $(DOWNLOADS_DIR):/var/downloads$(VOLUME_MOUNT_OPTIONS) --build-arg MQ_URL=file:///var/downloads/$4,$5)
|
||||
endef
|
||||
|
||||
# When building with Podman, just pass the downloads directory as a volume
|
||||
# Args: imageName, imageTag, dockerfile, mqArchive, dockerfileTarget
|
||||
define build-mq-podman
|
||||
$(call build-mq-podman-$(shell uname -s),$1,$2,$3,$4,$5)
|
||||
endef
|
||||
|
||||
# Build an MQ image. The commands used are slightly different between Docker and Podman
|
||||
# Args: imageName, imageTag, dockerfile, mqArchive, dockerfileTarget
|
||||
define build-mq
|
||||
$(call build-mq-$(COMMAND),$1,$2,$3,$4,$5)
|
||||
endef
|
||||
|
||||
###############################################################################
|
||||
# Build targets
|
||||
###############################################################################
|
||||
@@ -584,33 +533,20 @@ lint: $(addsuffix /$(wildcard *.go), $(GO_PKG_DIRS))
|
||||
.PHONY: gosec
|
||||
gosec:
|
||||
$(info $(SPACER)$(shell printf "Running gosec test"$(END)))
|
||||
@gosec -fmt=json -out=gosec_results.json cmd/... internal/... 2> /dev/null ;\
|
||||
cat "gosec_results.json" ;\
|
||||
cat gosec_results.json | grep HIGH | grep severity > /dev/null ;\
|
||||
if [ $$? -eq 0 ]; then \
|
||||
printf "\nFAILURE: gosec found files containing HIGH severity issues - see results.json\n" ;\
|
||||
@gosecrc=0; gosec -fmt=json -out=gosec_results.json cmd/... internal/... 2> /dev/null || gosecrc=$$?; \
|
||||
cat gosec_results.json | jq '{"GolangErrors": (.["Golang errors"]|length>0),"Issues":(.Issues|length>0)}' | grep 'true' >/dev/null ;\
|
||||
if [ $$? -eq 0 ] || [ $$gosecrc -ne 0 ]; then \
|
||||
printf "FAILURE: Issues found running gosec - see gosec_results.json\n" ;\
|
||||
cat "gosec_results.json" ;\
|
||||
exit 1 ;\
|
||||
else \
|
||||
printf "\ngosec found no HIGH severity issues\n" ;\
|
||||
fi ;\
|
||||
cat gosec_results.json | grep MEDIUM | grep severity > /dev/null ;\
|
||||
if [ $$? -eq 0 ]; then \
|
||||
printf "\nFAILURE: gosec found files containing MEDIUM severity issues - see results.json\n" ;\
|
||||
exit 1 ;\
|
||||
else \
|
||||
printf "\ngosec found no MEDIUM severity issues\n" ;\
|
||||
fi ;\
|
||||
cat gosec_results.json | grep LOW | grep severity > /dev/null;\
|
||||
if [ $$? -eq 0 ]; then \
|
||||
printf "\nFAILURE: gosec found files containing LOW severity issues - see results.json\n" ;\
|
||||
exit 1;\
|
||||
else \
|
||||
printf "\ngosec found no LOW severity issues\n" ;\
|
||||
fi ;\
|
||||
printf "gosec found no issues\n" ;\
|
||||
cat "gosec_results.json" ;\
|
||||
fi
|
||||
|
||||
.PHONY: update-release-information
|
||||
update-release-information:
|
||||
sed -i.bak 's/ARG MQ_URL=.*-LinuxX64.tar.gz"/ARG MQ_URL="https:\/\/public.dhe.ibm.com\/ibmdl\/export\/pub\/software\/websphere\/messaging\/mqadv\/$(MQ_VERSION)-IBM-MQ-Advanced-for-Developers-Non-Install-LinuxX64.tar.gz"/g' Dockerfile-server && rm Dockerfile-server.bak
|
||||
sed -i.bak 's/ARG MQ_ARCHIVE=.*-LinuxX64.tar.gz"/ARG MQ_ARCHIVE="downloads\/$(MQ_VERSION)-IBM-MQ-Advanced-for-Developers-Non-Install-LinuxX64.tar.gz"/g' Dockerfile-server && rm Dockerfile-server.bak
|
||||
$(eval MQ_VERSION_1=$(shell echo '${MQ_VERSION}' | rev | cut -c 3- | rev))
|
||||
sed -i.bak 's/IBM_MQ_.*_LINUX_X86-64_NOINST.tar.gz/IBM_MQ_${MQ_VERSION_1}_LINUX_X86-64_NOINST.tar.gz/g' docs/building.md && rm docs/building.md.bak
|
||||
sed -i.bak 's/ibm-mqadvanced-server:.*-amd64/ibm-mqadvanced-server:$(MQ_VERSION)-amd64/g' docs/security.md
|
||||
|
||||
11
README.md
11
README.md
@@ -27,7 +27,10 @@ Note that in order to use the image, it is necessary to accept the terms of the
|
||||
- **LICENSE** - Set this to `accept` to agree to the MQ Advanced for Developers license. If you wish to see the license you can set this to `view`.
|
||||
- **LANG** - Set this to the language you would like the license to be printed in.
|
||||
- **MQ_QMGR_NAME** - Set this to the name you want your Queue Manager to be created with.
|
||||
- **LOG_FORMAT** - Set this to change the format of the logs which are printed on the container's stdout. Set to "json" to use JSON format (JSON object per line); set to "basic" to use a simple human-readable format. Defaults to "basic".
|
||||
- **MQ_QMGR_LOG_FILE_PAGES** - Set this to control the value for LogFilePages passed to the "crtmqm" command. Cannot be changed after queue manager creation.
|
||||
- **MQ_LOGGING_CONSOLE_SOURCE** - Specifies a comma-separated list of sources for logs which are mirrored to the container's stdout. The valid values are "qmgr" and "web". Defaults to "qmgr".
|
||||
- **MQ_LOGGING_CONSOLE_FORMAT** - Changes the format of the logs which are printed on the container's stdout. Set to "json" to use JSON format (JSON object per line); set to "basic" to use a simple human-readable format. Defaults to "basic".
|
||||
- **MQ_LOGGING_CONSOLE_EXCLUDE_ID** - Excludes log messages with the specified ID. The log messages still appear in the log file on disk, but are excluded from the container's stdout. Defaults to "AMQ5041I,AMQ5052I,AMQ5051I,AMQ5037I,AMQ5975I".
|
||||
- **MQ_ENABLE_METRICS** - Set this to `true` to generate Prometheus metrics for your Queue Manager.
|
||||
|
||||
See the [default developer configuration docs](docs/developer-config.md) for the extra environment variables supported by the MQ Advanced for Developers image.
|
||||
@@ -38,7 +41,7 @@ If you want to use IBM MQ on [Kubernetes](https://kubernetes.io), you can find a
|
||||
|
||||
## Issues and contributions
|
||||
|
||||
For issues relating specifically to the container image or Helm chart, please use the [GitHub issue tracker](https://github.com/ibm-messaging/mq-container/issues). If you do submit a Pull Request related to this Docker image, please indicate in the Pull Request that you accept and agree to be bound by the terms of the [IBM Contributor License Agreement](CLA.md).
|
||||
For issues relating specifically to the container image or Helm chart, please use the [GitHub issue tracker](https://github.com/ibm-messaging/mq-container/issues). Pull requests are not currently accepted.
|
||||
|
||||
## License
|
||||
|
||||
@@ -46,11 +49,11 @@ The Dockerfiles and associated code and scripts are licensed under the [Apache L
|
||||
Licenses for the products installed within the images are as follows:
|
||||
|
||||
- [IBM MQ Advanced for Developers](http://www14.software.ibm.com/cgi-bin/weblap/lap.pl?la_formnum=Z125-3301-14&li_formnum=L-APIG-CAUEQC) (International License Agreement for Non-Warranted Programs). This license may be viewed from an image using the `LICENSE=view` environment variable as described above or by following the link above.
|
||||
- [IBM MQ Advanced](http://www14.software.ibm.com/cgi-bin/weblap/lap.pl?la_formnum=Z125-3301-14&li_formnum=L-APIG-CAUEBE) (International Program License Agreement). This license may be viewed from an image using the `LICENSE=view` environment variable as described above or by following the link above.
|
||||
- [IBM MQ Advanced](http://www14.software.ibm.com/cgi-bin/weblap/lap.pl?la_formnum=Z125-3301-14&li_formnum=L-UPFX-8MW49T) (International Program License Agreement). This license may be viewed from an image using the `LICENSE=view` environment variable as described above or by following the link above.
|
||||
|
||||
Note: The IBM MQ Advanced for Developers license does not permit further distribution and the terms restrict usage to a developer machine.
|
||||
|
||||
|
||||
## Copyright
|
||||
|
||||
© Copyright IBM Corporation 2015, 2022
|
||||
© Copyright IBM Corporation 2015, 2023
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2017, 2022
|
||||
© Copyright IBM Corporation 2017, 2023
|
||||
|
||||
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,12 @@ func doMain() int {
|
||||
}
|
||||
|
||||
// Check if the queue manager has a running listener
|
||||
if active, _ := ready.IsRunningAsActiveQM(ctx, name); active {
|
||||
status, err := ready.Status(ctx, name)
|
||||
if err != nil {
|
||||
return 1
|
||||
}
|
||||
switch status {
|
||||
case ready.StatusActiveQM:
|
||||
conn, err := net.Dial("tcp", "127.0.0.1:1414")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
@@ -54,16 +59,16 @@ func doMain() int {
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
} else if standby, _ := ready.IsRunningAsStandbyQM(ctx, name); standby {
|
||||
return 0
|
||||
case ready.StatusStandbyQM:
|
||||
fmt.Printf("Detected queue manager running in standby mode")
|
||||
return 10
|
||||
} else if replica, _ := ready.IsRunningAsReplicaQM(ctx, name); replica {
|
||||
case ready.StatusReplicaQM:
|
||||
fmt.Printf("Detected queue manager running in replica mode")
|
||||
return 20
|
||||
} else {
|
||||
default:
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2018, 2021
|
||||
© Copyright IBM Corporation 2018, 2023
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/ibm-messaging/mq-container/internal/htpasswd"
|
||||
@@ -30,7 +31,20 @@ import (
|
||||
var log *logger.Logger
|
||||
|
||||
func getLogFormat() string {
|
||||
return os.Getenv("LOG_FORMAT")
|
||||
logFormat := strings.ToLower(strings.TrimSpace(os.Getenv("MQ_LOGGING_CONSOLE_FORMAT")))
|
||||
//old-style env var is used.
|
||||
if logFormat == "" {
|
||||
logFormat = strings.ToLower(strings.TrimSpace(os.Getenv("LOG_FORMAT")))
|
||||
}
|
||||
|
||||
if logFormat != "" && (logFormat == "basic" || logFormat == "json") {
|
||||
return logFormat
|
||||
} else {
|
||||
//this is the case where value is either empty string or set to something other than "basic"/"json"
|
||||
logFormat = "basic"
|
||||
}
|
||||
|
||||
return logFormat
|
||||
}
|
||||
|
||||
func getDebug() bool {
|
||||
@@ -77,6 +91,7 @@ func logTermination(args ...interface{}) {
|
||||
// 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)
|
||||
// #nosec G306 - its a read by owner/s group, and pose no harm.
|
||||
err := ioutil.WriteFile("/run/termination-log", []byte(msg), 0660)
|
||||
if err != nil {
|
||||
log.Debug(err)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2017, 2022
|
||||
© Copyright IBM Corporation 2017, 2023
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -46,6 +46,7 @@ func logTermination(args ...interface{}) {
|
||||
// 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)
|
||||
// #nosec G306 - its a read by owner/s group, and pose no harm.
|
||||
err := ioutil.WriteFile("/run/termination-log", []byte(msg), 0660)
|
||||
if err != nil {
|
||||
log.Debug(err)
|
||||
@@ -58,7 +59,20 @@ func logTermination(args ...interface{}) {
|
||||
}
|
||||
|
||||
func getLogFormat() string {
|
||||
return os.Getenv("LOG_FORMAT")
|
||||
logFormat := strings.ToLower(strings.TrimSpace(os.Getenv("MQ_LOGGING_CONSOLE_FORMAT")))
|
||||
//old-style env var is used.
|
||||
if logFormat == "" {
|
||||
logFormat = strings.ToLower(strings.TrimSpace(os.Getenv("LOG_FORMAT")))
|
||||
}
|
||||
|
||||
if logFormat != "" && (logFormat == "basic" || logFormat == "json") {
|
||||
return logFormat
|
||||
} else {
|
||||
//this is the case where value is either empty string or set to something other than "basic"/"json"
|
||||
logFormat = "basic"
|
||||
}
|
||||
|
||||
return logFormat
|
||||
}
|
||||
|
||||
// formatBasic formats a log message parsed from JSON, as "basic" text
|
||||
@@ -81,6 +95,94 @@ func formatBasic(obj map[string]interface{}) string {
|
||||
}
|
||||
// Convert time zone information from some logs (e.g. Liberty) for consistency
|
||||
obj["ibm_datetime"] = strings.Replace(obj["ibm_datetime"].(string), "+0000", "Z", 1)
|
||||
// Escape any new-line characters, so that we don't get multi-line messages messing up the output
|
||||
obj["message"] = strings.ReplaceAll(obj["message"].(string), "\n", "\\n")
|
||||
|
||||
if obj["type"] != nil && (obj["type"] == "liberty_trace") {
|
||||
timeStamp := obj["ibm_datetime"]
|
||||
threadID := ""
|
||||
srtModuleName := ""
|
||||
logLevel := ""
|
||||
ibmClassName := ""
|
||||
srtIbmClassName := ""
|
||||
ibmMethodName := ""
|
||||
message := ""
|
||||
|
||||
if obj["loglevel"] != nil {
|
||||
//threadID is captured below
|
||||
if obj["ibm_threadId"] != nil {
|
||||
threadID = obj["ibm_threadId"].(string)
|
||||
}
|
||||
|
||||
//logLevel character to be mirrored in console web server logging is decided below
|
||||
logLevelTmp := obj["loglevel"].(string)
|
||||
switch logLevelTmp {
|
||||
case "AUDIT":
|
||||
logLevel = "A"
|
||||
case "INFO":
|
||||
logLevel = "I"
|
||||
case "EVENT":
|
||||
logLevel = "1"
|
||||
case "ENTRY":
|
||||
logLevel = ">"
|
||||
case "EXIT":
|
||||
logLevel = "<"
|
||||
case "FINE":
|
||||
logLevel = "1"
|
||||
case "FINER":
|
||||
logLevel = "2"
|
||||
case "FINEST":
|
||||
logLevel = "3"
|
||||
default:
|
||||
logLevel = string(logLevelTmp[0])
|
||||
}
|
||||
|
||||
//This is a 13 characters string present in extracted out of module node
|
||||
if obj["module"] != nil {
|
||||
srtModuleNameArr := strings.Split(obj["module"].(string), ".")
|
||||
arrLen := len(srtModuleNameArr)
|
||||
srtModuleName = srtModuleNameArr[arrLen-1]
|
||||
if len(srtModuleName) > 13 {
|
||||
srtModuleName = srtModuleName[0:13]
|
||||
}
|
||||
}
|
||||
if obj["ibm_className"] != nil {
|
||||
ibmClassName = obj["ibm_className"].(string)
|
||||
|
||||
//A 13 character string is extracted from class name. This is required for FINE, FINER & FINEST log lines
|
||||
ibmClassNameArr := strings.Split(ibmClassName, ".")
|
||||
arrLen := len(ibmClassNameArr)
|
||||
srtIbmClassName = ibmClassNameArr[arrLen-1]
|
||||
if len(srtModuleName) > 13 {
|
||||
srtIbmClassName = srtIbmClassName[0:13]
|
||||
}
|
||||
}
|
||||
if obj["ibm_methodName"] != nil {
|
||||
ibmMethodName = obj["ibm_methodName"].(string)
|
||||
}
|
||||
if obj["message"] != nil {
|
||||
message = obj["message"].(string)
|
||||
}
|
||||
|
||||
//For AUDIT & INFO logging
|
||||
if logLevel == "A" || logLevel == "I" {
|
||||
return fmt.Sprintf("%s %s %-13s %s %s %s %s\n", timeStamp, threadID, srtModuleName, logLevel, ibmClassName, ibmMethodName, message)
|
||||
}
|
||||
//For EVENT logLevel
|
||||
if logLevelTmp == "EVENT" {
|
||||
return fmt.Sprintf("%s %s %-13s %s %s\n", timeStamp, threadID, srtModuleName, logLevel, message)
|
||||
}
|
||||
//For ENTRY & EXIT
|
||||
if logLevel == ">" || logLevel == "<" {
|
||||
return fmt.Sprintf("%s %s %-13s %s %s %s\n", timeStamp, threadID, srtModuleName, logLevel, ibmMethodName, message)
|
||||
}
|
||||
//For deeper log levels
|
||||
if logLevelTmp == "FINE" || logLevel == "2" || logLevel == "3" {
|
||||
return fmt.Sprintf("%s %s %-13s %s %s %s %s\n", timeStamp, threadID, srtIbmClassName, logLevel, ibmClassName, ibmMethodName, message)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf("%s %s\n", obj["ibm_datetime"], obj["message"])
|
||||
}
|
||||
|
||||
@@ -131,6 +233,11 @@ func configureLogger(name string) (mirrorFunc, error) {
|
||||
return nil, err
|
||||
}
|
||||
return func(msg string, isQMLog bool) bool {
|
||||
arrLoggingConsoleExcludeIds := strings.Split(strings.ToUpper(os.Getenv("MQ_LOGGING_CONSOLE_EXCLUDE_ID")), ",")
|
||||
if isExcludedMsgIdPresent(msg, arrLoggingConsoleExcludeIds) {
|
||||
//If excluded id is present do not mirror it, return back
|
||||
return false
|
||||
}
|
||||
// Check if the message is JSON
|
||||
if len(msg) > 0 && msg[0] == '{' {
|
||||
obj, err := processLogMessage(msg)
|
||||
@@ -155,6 +262,11 @@ func configureLogger(name string) (mirrorFunc, error) {
|
||||
return nil, err
|
||||
}
|
||||
return func(msg string, isQMLog bool) bool {
|
||||
arrLoggingConsoleExcludeIds := strings.Split(strings.ToUpper(os.Getenv("MQ_LOGGING_CONSOLE_EXCLUDE_ID")), ",")
|
||||
if isExcludedMsgIdPresent(msg, arrLoggingConsoleExcludeIds) {
|
||||
//If excluded id is present do not mirror it, return back
|
||||
return false
|
||||
}
|
||||
// Check if the message is JSON
|
||||
if len(msg) > 0 && msg[0] == '{' {
|
||||
// Parse the JSON message, and print a simplified version
|
||||
@@ -197,6 +309,16 @@ func filterQMLogMessage(obj map[string]interface{}) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Function to check if ids provided in MQ_LOGGING_CONSOLE_EXCLUDE_ID are present in given log line or not
|
||||
func isExcludedMsgIdPresent(msg string, envExcludeIds []string) bool {
|
||||
for _, id := range envExcludeIds {
|
||||
if id != "" && strings.Contains(msg, strings.TrimSpace(id)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func logDiagnostics() {
|
||||
if getDebug() {
|
||||
log.Debug("--- Start Diagnostics ---")
|
||||
@@ -238,3 +360,73 @@ func logDiagnostics() {
|
||||
log.Debug("--- End Diagnostics ---")
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the value of MQ_LOGGING_CONSOLE_SOURCE environment variable
|
||||
func getMQLogConsoleSource() string {
|
||||
return strings.ToLower(strings.TrimSpace(os.Getenv("MQ_LOGGING_CONSOLE_SOURCE")))
|
||||
|
||||
}
|
||||
|
||||
// Function to check if valid values are provided for environment variable MQ_LOGGING_CONSOLE_SOURCE. If not valid, main program throws a warning to console
|
||||
func isLogConsoleSourceValid() bool {
|
||||
mqLogSource := getMQLogConsoleSource()
|
||||
retValue := false
|
||||
//If nothing is set, we will mirror qmgr, so valid
|
||||
if mqLogSource == "" {
|
||||
return true
|
||||
}
|
||||
|
||||
logConsoleSource := strings.Split(mqLogSource, ",")
|
||||
//This will find out if the environment variable contains permitted values and is comma separated
|
||||
for _, src := range logConsoleSource {
|
||||
switch strings.TrimSpace(src) {
|
||||
//If it is a permitted value, it is valid. Keep it as true, but dont return it. We may encounter something junk soon
|
||||
case "qmgr", "web", "":
|
||||
retValue = true
|
||||
//If invalid entry arrives in-between/anywhere, just return false, there is no turning back
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return retValue
|
||||
}
|
||||
|
||||
// To check which all logs have to be mirrored
|
||||
func checkLogSourceForMirroring(source string) bool {
|
||||
logsrcs := getMQLogConsoleSource()
|
||||
|
||||
//Nothing set, this is when we mirror qmgr
|
||||
if logsrcs == "" {
|
||||
if source == "qmgr" {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//Split the csv environment value so that we get an accurate comparison instead of a contains() check
|
||||
logSrcArr := strings.Split(logsrcs, ",")
|
||||
|
||||
//Iterate through the array to decide on mirroring
|
||||
for _, arr := range logSrcArr {
|
||||
switch strings.TrimSpace(arr) {
|
||||
case "qmgr":
|
||||
//If value of source is qmgr and it exists in environment variable, mirror qmgr logs
|
||||
if source == "qmgr" {
|
||||
return true
|
||||
}
|
||||
case "web":
|
||||
//If value of source is web and it exists in environment variable, and mirror web logs
|
||||
if source == "web" {
|
||||
//If older environment variable is set make sure to print appropriate message
|
||||
if os.Getenv("MQ_ENABLE_EMBEDDED_WEB_SERVER_LOG") != "" {
|
||||
log.Println("Environment variable MQ_ENABLE_EMBEDDED_WEB_SERVER_LOG has now been replaced. Use MQ_LOGGING_CONSOLE_SOURCE instead.")
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2020
|
||||
© Copyright IBM Corporation 2020, 2023
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -18,6 +18,7 @@ package main
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
@@ -53,3 +54,86 @@ func TestFormatBasic(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// This test covers for functions isLogConsoleSourceValid() & checkLogSourceForMirroring()
|
||||
var mqLogSourcesTests = []struct {
|
||||
testNum int
|
||||
logsrc string
|
||||
exptValid bool
|
||||
exptQmgrSrc bool
|
||||
exptWebSrc bool
|
||||
}{
|
||||
{1, "qmgr,web", true, true, true},
|
||||
{2, "qmgr", true, true, false},
|
||||
{3, "web,qmgr", true, true, true},
|
||||
{4, "web", true, false, true},
|
||||
{5, " ", true, true, false},
|
||||
{6, "QMGR,WEB", true, true, true},
|
||||
{7, "qmgr, ", true, true, false},
|
||||
{8, "qmgr , web", true, true, true},
|
||||
{9, "qmgr,dummy", false, true, false},
|
||||
{10, "fake,dummy", false, false, false},
|
||||
{11, "qmgr,fake,dummy", false, true, false},
|
||||
{12, "fake,dummy,web", false, false, true},
|
||||
{13, "true", false, false, false},
|
||||
{14, "false", false, false, false},
|
||||
{15, "", true, true, false},
|
||||
}
|
||||
|
||||
func TestLoggingConsoleSourceInputs(t *testing.T) {
|
||||
for _, mqlogsrctest := range mqLogSourcesTests {
|
||||
err := os.Setenv("MQ_LOGGING_CONSOLE_SOURCE", mqlogsrctest.logsrc)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
isValid := isLogConsoleSourceValid()
|
||||
if isValid != mqlogsrctest.exptValid {
|
||||
t.Errorf("Expected return value from isLogConsoleSourceValid() is %v for MQ_LOGGING_CONSOLE_SOURCE='%v', got %v\n", mqlogsrctest.exptValid, mqlogsrctest.logsrc, isValid)
|
||||
}
|
||||
isLogSrcQmgr := checkLogSourceForMirroring("qmgr")
|
||||
if isLogSrcQmgr != mqlogsrctest.exptQmgrSrc {
|
||||
t.Errorf("Expected return value from checkLogSourceForMirroring() is %v for MQ_LOGGING_CONSOLE_SOURCE='%v', got %v\n", mqlogsrctest.exptQmgrSrc, mqlogsrctest.logsrc, isLogSrcQmgr)
|
||||
}
|
||||
isLogSrcWeb := checkLogSourceForMirroring("web")
|
||||
if isLogSrcWeb != mqlogsrctest.exptWebSrc {
|
||||
t.Errorf("Expected return value from checkLogSourceForMirroring() is %v for MQ_LOGGING_CONSOLE_SOURCE='%v', got %v\n", mqlogsrctest.exptWebSrc, mqlogsrctest.logsrc, isLogSrcWeb)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This test covers for function isExcludedMsgIdPresent()
|
||||
var mqExcludeIDTests = []struct {
|
||||
testNum int
|
||||
exculdeIDsArr []string
|
||||
expectedRetVal bool
|
||||
logEntry string
|
||||
}{
|
||||
{
|
||||
1,
|
||||
[]string{"AMQ5051I", "AMQ5037I", "AMQ5975I"},
|
||||
true,
|
||||
"{\"ibm_messageId\":\"AMQ5051I\",\"ibm_arithInsert1\":0,\"ibm_arithInsert2\":1,\"message\":\"AMQ5051I: The queue manager task 'AUTOCONFIG' has started.\"}",
|
||||
},
|
||||
{
|
||||
2,
|
||||
[]string{"AMQ5975I", "AMQ5037I"},
|
||||
false,
|
||||
"{\"ibm_messageId\":\"AMQ5051I\",\"ibm_arithInsert1\":0,\"ibm_arithInsert2\":1,\"message\":\"AMQ5051I: The queue manager task 'AUTOCONFIG' has started.\"}",
|
||||
},
|
||||
{
|
||||
3,
|
||||
[]string{""},
|
||||
false,
|
||||
"{\"ibm_messageId\":\"AMQ5051I\",\"ibm_arithInsert1\":0,\"ibm_arithInsert2\":1,\"message\":\"AMQ5051I: The queue manager task 'AUTOCONFIG' has started.\"}",
|
||||
},
|
||||
}
|
||||
|
||||
func TestIsExcludedMsgIDPresent(t *testing.T) {
|
||||
for _, excludeIDTest := range mqExcludeIDTests {
|
||||
retVal := isExcludedMsgIdPresent(excludeIDTest.logEntry, excludeIDTest.exculdeIDsArr)
|
||||
if retVal != excludeIDTest.expectedRetVal {
|
||||
t.Errorf("%v. Expected return value from isExcludedMsgIdPresent() is %v for MQ_LOGGING_CONSOLE_EXCLUDE_ID='%v', got %v\n",
|
||||
excludeIDTest.testNum, excludeIDTest.expectedRetVal, excludeIDTest.exculdeIDsArr, retVal)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2017, 2021
|
||||
© Copyright IBM Corporation 2017, 2023
|
||||
|
||||
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 (
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/ibm-messaging/mq-container/internal/fips"
|
||||
"github.com/ibm-messaging/mq-container/internal/ha"
|
||||
"github.com/ibm-messaging/mq-container/internal/metrics"
|
||||
"github.com/ibm-messaging/mq-container/internal/ready"
|
||||
@@ -144,6 +145,9 @@ func doMain() error {
|
||||
// Print out versioning information
|
||||
logVersionInfo()
|
||||
|
||||
// Determine FIPS compliance level
|
||||
fips.ProcessFIPSType(log)
|
||||
|
||||
keyLabel, defaultCmsKeystore, defaultP12Truststore, err := tls.ConfigureDefaultTLSKeystores()
|
||||
if err != nil {
|
||||
logTermination(err)
|
||||
@@ -156,6 +160,11 @@ func doMain() error {
|
||||
return err
|
||||
}
|
||||
|
||||
//Validate MQ_LOG_CONSOLE_SOURCE variable
|
||||
if !isLogConsoleSourceValid() {
|
||||
log.Println("One or more invalid value is provided for MQ_LOGGING_CONSOLE_SOURCE. Allowed values are 'qmgr' & 'web' in csv format")
|
||||
}
|
||||
|
||||
err = postInit(name, keyLabel, defaultP12Truststore)
|
||||
if err != nil {
|
||||
logTermination(err)
|
||||
@@ -170,6 +179,9 @@ func doMain() error {
|
||||
}
|
||||
}
|
||||
|
||||
// Post FIPS initialization processing
|
||||
fips.PostInit(log)
|
||||
|
||||
enableTraceCrtmqm := os.Getenv("MQ_ENABLE_TRACE_CRTMQM")
|
||||
if enableTraceCrtmqm == "true" || enableTraceCrtmqm == "1" {
|
||||
err = startMQTrace()
|
||||
@@ -203,17 +215,24 @@ func doMain() error {
|
||||
log.Debug("Cancel log mirroring")
|
||||
cancelMirror()
|
||||
}()
|
||||
// TODO: Use the error channel
|
||||
_, err = mirrorSystemErrorLogs(ctx, &wg, mf)
|
||||
if err != nil {
|
||||
logTermination(err)
|
||||
return err
|
||||
}
|
||||
_, err = mirrorQueueManagerErrorLogs(ctx, &wg, name, newQM, mf)
|
||||
if err != nil {
|
||||
logTermination(err)
|
||||
return err
|
||||
|
||||
//For mirroring mq system logs and qm logs, if environment variable is set
|
||||
if checkLogSourceForMirroring("qmgr") {
|
||||
//Mirror MQ system logs
|
||||
_, err = mirrorSystemErrorLogs(ctx, &wg, mf)
|
||||
if err != nil {
|
||||
logTermination(err)
|
||||
return err
|
||||
}
|
||||
|
||||
//Mirror queue manager logs
|
||||
_, err = mirrorQueueManagerErrorLogs(ctx, &wg, name, newQM, mf)
|
||||
if err != nil {
|
||||
logTermination(err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if *devFlag {
|
||||
_, err = mirrorHTPasswdLogs(ctx, &wg, name, newQM, mf)
|
||||
if err != nil {
|
||||
@@ -221,10 +240,12 @@ func doMain() error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// Recommended to use this option in conjunction with setting WLP_LOGGING_MESSAGE_FORMAT=JSON
|
||||
mirrorWebLog := os.Getenv("MQ_ENABLE_EMBEDDED_WEB_SERVER_LOG")
|
||||
if mirrorWebLog == "true" || mirrorWebLog == "1" {
|
||||
_, err = mirrorWebServerLogs(ctx, &wg, name, newQM, mf)
|
||||
|
||||
//For mirroring web server logs if source variable is set
|
||||
if checkLogSourceForMirroring("web") {
|
||||
// Always log from the start of the web server messages.log, as
|
||||
// Liberty resets it.
|
||||
_, err = mirrorWebServerLogs(ctx, &wg, name, true, mf)
|
||||
if err != nil {
|
||||
logTermination(err)
|
||||
return err
|
||||
|
||||
@@ -95,6 +95,7 @@ func mirrorLog(ctx context.Context, wg *sync.WaitGroup, path string, fromStart b
|
||||
// the file is open before the queue manager is created or started.
|
||||
// Otherwise, there would be the potential for a nearly-full file to
|
||||
// rotate before the goroutine had a chance to open it.
|
||||
// #nosec G304 - no harm, we open readonly and check error.
|
||||
f, err = os.OpenFile(path, os.O_RDONLY, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -122,6 +123,7 @@ func mirrorLog(ctx context.Context, wg *sync.WaitGroup, path string, fromStart b
|
||||
return
|
||||
}
|
||||
log.Debugf("File exists: %v, %v", path, fi.Size())
|
||||
// #nosec G304 - no harm, we open readonly and check error.
|
||||
f, err = os.OpenFile(path, os.O_RDONLY, 0)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
@@ -169,6 +171,7 @@ func mirrorLog(ctx context.Context, wg *sync.WaitGroup, path string, fromStart b
|
||||
}
|
||||
// Re-open file
|
||||
log.Debugf("Re-opening error log file %v", path)
|
||||
// #nosec G304 - no harm, we open readonly and check error.
|
||||
f, err = os.OpenFile(path, os.O_RDONLY, 0)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2018, 2019
|
||||
© Copyright IBM Corporation 2018, 2023
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -18,6 +18,7 @@ package main
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/ibm-messaging/mq-container/internal/fips"
|
||||
"github.com/ibm-messaging/mq-container/internal/tls"
|
||||
)
|
||||
|
||||
@@ -25,6 +26,15 @@ import (
|
||||
func postInit(name, keyLabel string, p12Truststore tls.KeyStoreData) error {
|
||||
enableWebServer := os.Getenv("MQ_ENABLE_EMBEDDED_WEB_SERVER")
|
||||
if enableWebServer == "true" || enableWebServer == "1" {
|
||||
|
||||
// Enable FIPS for MQ Web Server if asked for.
|
||||
if fips.IsFIPSEnabled() {
|
||||
err := configureFIPSWebServer(p12Truststore)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Configure the web server (if enabled)
|
||||
webKeystore, err := configureWebServer(keyLabel, p12Truststore)
|
||||
if err != nil {
|
||||
@@ -35,6 +45,7 @@ func postInit(name, keyLabel string, p12Truststore tls.KeyStoreData) error {
|
||||
if len(p12Truststore.TrustedCerts) == 0 {
|
||||
webTruststoreRef = "MQWebKeyStore"
|
||||
}
|
||||
|
||||
// Start the web server, in the background (if installed)
|
||||
// WARNING: No error handling or health checking available for the web server
|
||||
go func() {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2017, 2022
|
||||
© Copyright IBM Corporation 2017, 2023
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -55,23 +55,36 @@ func createDirStructure() error {
|
||||
func createQueueManager(name string, devMode bool) (bool, error) {
|
||||
log.Printf("Creating queue manager %v", name)
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
mounts, err := containerruntime.GetMounts()
|
||||
if err != nil {
|
||||
log.Printf("Error getting mounts for queue manager")
|
||||
return false, err
|
||||
}
|
||||
|
||||
dataDir := getQueueManagerDataDir(mounts, name)
|
||||
|
||||
// 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)
|
||||
// Check if MQ_QMGR_LOG_FILE_PAGES matches the value set in qm.ini
|
||||
lfp := os.Getenv("MQ_QMGR_LOG_FILE_PAGES")
|
||||
if lfp != "" {
|
||||
qmIniBytes, err := readQMIni(dataDir)
|
||||
if err != nil {
|
||||
log.Printf("Error reading qm.ini : %v", err)
|
||||
return false, err
|
||||
}
|
||||
if !validateLogFilePageSetting(qmIniBytes, lfp) {
|
||||
log.Println("Warning: the value of MQ_QMGR_LOG_FILE_PAGES does not match the value of 'LogFilePages' in the qm.ini. This setting cannot be altered after Queue Manager creation.")
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// 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
|
||||
@@ -96,6 +109,25 @@ func createQueueManager(name string, devMode bool) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
//readQMIni reads the qm.ini file and returns it as a byte array
|
||||
//This function is specific to comply with the nosec.
|
||||
func readQMIni(dataDir string) ([]byte, error) {
|
||||
qmgrDir := filepath.Join(dataDir, "qm.ini")
|
||||
// #nosec G304 - qmgrDir filepath is derived from dspmqinf
|
||||
iniFileBytes, err := ioutil.ReadFile(qmgrDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return iniFileBytes, err
|
||||
}
|
||||
|
||||
//validateLogFilePageSetting validates if the specified logFilePage number is equal to the existing value in the qm.ini
|
||||
func validateLogFilePageSetting(iniFileBytes []byte, logFilePages string) bool {
|
||||
lfpString := "LogFilePages=" + logFilePages
|
||||
qminiConfigStr := string(iniFileBytes)
|
||||
return strings.Contains(qminiConfigStr, lfpString)
|
||||
}
|
||||
|
||||
func updateCommandLevel() error {
|
||||
level, ok := os.LookupEnv("MQ_CMDLEVEL")
|
||||
if ok && level != "" {
|
||||
@@ -132,11 +164,12 @@ func startQueueManager(name string) error {
|
||||
func stopQueueManager(name string) error {
|
||||
log.Println("Stopping queue manager")
|
||||
qmGracePeriod := os.Getenv("MQ_GRACE_PERIOD")
|
||||
isStandby, err := ready.IsRunningAsStandbyQM(context.Background(), name)
|
||||
status, err := ready.Status(context.Background(), name)
|
||||
if err != nil {
|
||||
log.Printf("Error getting status for queue manager %v: %v", name, err.Error())
|
||||
return err
|
||||
}
|
||||
isStandby := status.StandbyQM()
|
||||
args := []string{"-w", "-r", "-tp", qmGracePeriod, name}
|
||||
if os.Getenv("MQ_MULTI_INSTANCE") == "true" {
|
||||
if isStandby {
|
||||
@@ -238,6 +271,14 @@ func getCreateQueueManagerArgs(mounts map[string]string, name string, devMode bo
|
||||
if _, ok := mounts["/mnt/mqm-data"]; ok {
|
||||
args = append(args, "-md", "/mnt/mqm-data/qmgrs")
|
||||
}
|
||||
if os.Getenv("MQ_QMGR_LOG_FILE_PAGES") != "" {
|
||||
_, err = strconv.Atoi(os.Getenv("MQ_QMGR_LOG_FILE_PAGES"))
|
||||
if err != nil {
|
||||
log.Printf("Error processing MQ_QMGR_LOG_FILE_PAGES, the default value for LogFilePages will be used. Err: %v", err)
|
||||
} else {
|
||||
args = append(args, "-lf", os.Getenv("MQ_QMGR_LOG_FILE_PAGES"))
|
||||
}
|
||||
}
|
||||
args = append(args, name)
|
||||
return args
|
||||
}
|
||||
@@ -287,7 +328,8 @@ func updateQMini(qmname string) error {
|
||||
if strings.Contains(qminiConfigStr, "ServiceComponent:") {
|
||||
var re = regexp.MustCompile(`(?m)^.*ServiceComponent.*$\s^.*Service.*$\s^.*Name.*$\s^.*Module.*$\s^.*ComponentDataSize.*$`)
|
||||
curFile := re.ReplaceAllString(qminiConfigStr, "")
|
||||
// #nosec G304 - qmgrDir filepath is derived from dspmqinf
|
||||
// #nosec G304 G306 - qmgrDir filepath is derived from dspmqinf and
|
||||
// its a read by owner/s group, and pose no harm.
|
||||
err := ioutil.WriteFile(qmgrDir, []byte(curFile), 0660)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
86
cmd/runmqserver/qmgr_test.go
Normal file
86
cmd/runmqserver/qmgr_test.go
Normal file
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2023
|
||||
|
||||
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 (
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_validateLogFilePageSetting(t *testing.T) {
|
||||
type args struct {
|
||||
iniFilePath string
|
||||
isValid bool
|
||||
logFilePagesValue string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
}{
|
||||
{
|
||||
name: "TestLogFilePages1",
|
||||
args: args{
|
||||
iniFilePath: "./test-files/testvalidateLogFilePages_1.ini",
|
||||
isValid: true,
|
||||
logFilePagesValue: "1235",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "TestLogFilePages2",
|
||||
args: args{
|
||||
iniFilePath: "./test-files/testvalidateLogFilePages_2.ini",
|
||||
isValid: true,
|
||||
logFilePagesValue: "2224",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "TestLogFilePages3",
|
||||
args: args{
|
||||
iniFilePath: "./test-files/testvalidateLogFilePages_3.ini",
|
||||
isValid: false,
|
||||
logFilePagesValue: "1235",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "TestLogFilePages4",
|
||||
args: args{
|
||||
iniFilePath: "./test-files/testvalidateLogFilePages_4.ini",
|
||||
isValid: false,
|
||||
logFilePagesValue: "1235",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "TestLogFilePages5",
|
||||
args: args{
|
||||
iniFilePath: "./test-files/testvalidateLogFilePages_5.ini",
|
||||
isValid: false,
|
||||
logFilePagesValue: "1235",
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
iniFileBytes, err := ioutil.ReadFile(tt.args.iniFilePath)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
validate := validateLogFilePageSetting(iniFileBytes, tt.args.logFilePagesValue)
|
||||
if validate != tt.args.isValid {
|
||||
t.Fatalf("Expected ini file validation output to be %v got %v", tt.args.isValid, validate)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
ExitPath:
|
||||
ExitsDefaultPath=/mnt/mqm/data/exits
|
||||
ExitsDefaultPath64=/mnt/mqm/data/exits64
|
||||
Log:
|
||||
LogPrimaryFiles=3
|
||||
LogSecondaryFiles=2
|
||||
LogFilePages=1235
|
||||
LogBufferPages=0
|
||||
LogWriteIntegrity=TripleWrite
|
||||
@@ -0,0 +1,9 @@
|
||||
ExitPath:
|
||||
ExitsDefaultPath=/mnt/mqm/data/exits
|
||||
ExitsDefaultPath64=/mnt/mqm/data/exits64
|
||||
Log:
|
||||
LogPrimaryFiles=3
|
||||
LogSecondaryFiles=2
|
||||
LogFilePages=2224
|
||||
LogBufferPages=0
|
||||
LogWriteIntegrity=TripleWrite
|
||||
@@ -0,0 +1,9 @@
|
||||
ExitPath:
|
||||
ExitsDefaultPath=/mnt/mqm/data/exits
|
||||
ExitsDefaultPath64=/mnt/mqm/data/exits64
|
||||
Log:
|
||||
LogPrimaryFiles=3
|
||||
LogSecondaryFiles=2
|
||||
LogFilePages=6002
|
||||
LogBufferPages=0
|
||||
LogWriteIntegrity=TripleWrite
|
||||
@@ -0,0 +1,8 @@
|
||||
ExitPath:
|
||||
ExitsDefaultPath=/mnt/mqm/data/exits
|
||||
ExitsDefaultPath64=/mnt/mqm/data/exits64
|
||||
Log:
|
||||
LogPrimaryFiles=3
|
||||
LogSecondaryFiles=2
|
||||
LogBufferPages=0
|
||||
LogWriteIntegrity=TripleWrite
|
||||
@@ -0,0 +1,8 @@
|
||||
ExitPath:
|
||||
ExitsDefaultPath=/mnt/mqm/data/exits
|
||||
ExitsDefaultPath64=/mnt/mqm/data/exits64
|
||||
Log:
|
||||
LogPrimaryFiles=3
|
||||
LogSecondaryFiles=2
|
||||
LogBufferPages=1235
|
||||
LogWriteIntegrity=TripleWrite
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2018, 2020
|
||||
© Copyright IBM Corporation 2018, 2022
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -197,3 +197,25 @@ func configureWebServer(keyLabel string, p12Truststore tls.KeyStoreData) (string
|
||||
|
||||
return webKeystore, err
|
||||
}
|
||||
|
||||
// Configure FIPS mode for MQ Web Server
|
||||
func configureFIPSWebServer(p12TrustStore tls.KeyStoreData) error {
|
||||
var errOut error
|
||||
// Need to update jvm.options file of MQ Web Server. We don't update the jvm.options file
|
||||
// in /etc/mqm/web/installations/Installation1/servers/mqweb directory. Instead we update
|
||||
// the one in /etc/mqm/web/installations/Installation1/servers/mqweb/configDropins/defaults.
|
||||
// During runtime MQ Web Server merges the data from two files.
|
||||
mqwebJvmOptsDir := "/etc/mqm/web/installations/Installation1/servers/mqweb/configDropins/defaults"
|
||||
_, errOut = os.Stat(mqwebJvmOptsDir)
|
||||
if errOut == nil {
|
||||
// Update the jvm.options file using the data from template file. Tell the MQ Web Server
|
||||
// use a FIPS provider by setting "-Dcom.ibm.jsse2.usefipsprovider=true" and then tell it
|
||||
// use a specific FIPS provider by setting "Dcom.ibm.jsse2.usefipsProviderName=IBMJCEPlusFIPS".
|
||||
errOut = mqtemplate.ProcessTemplateFile(mqwebJvmOptsDir+"/jvm.options.tpl",
|
||||
mqwebJvmOptsDir+"/jvm.options", map[string]string{
|
||||
"FipsProvider": "true",
|
||||
"FipsProviderName": "IBMJCEPlusFIPS",
|
||||
}, log)
|
||||
}
|
||||
return errOut
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
###########################################################################################################################################################
|
||||
|
||||
# MQ_VERSION is the fully qualified MQ version number to build
|
||||
MQ_VERSION ?= 9.3.1.1
|
||||
MQ_VERSION ?= 9.3.2.0
|
||||
|
||||
###########################################################################################################################################################
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
You need to have the following tools installed:
|
||||
|
||||
* [Docker](https://www.docker.com/) 17.06.1 or later, or [Podman](https://podman.io) 1.0 or later (Podman 4.1 on macOS). If using Podman on macOS, the you need to be in "rootful" mode to allow the use of a network during builds. Run `podman machine init --rootful`.
|
||||
* [Docker](https://www.docker.com/) 17.06.1 or later, or [Podman](https://podman.io) 1.0 or later (Podman 4.1 on macOS).
|
||||
* [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.
|
||||
@@ -44,4 +44,4 @@ You can use the environment variable `MQ_ARCHIVE_DEV` to specify an alternative
|
||||
|
||||
## Installed components
|
||||
|
||||
This image includes the core MQ server, Java, language packs, GSKit, and web server. This is configured in the `Generate MQ package in INSTALLATION_DIR` section [here](../install-mq.sh), with the configured options being picked up at build time.
|
||||
This image includes the core MQ server, Java, language packs, GSKit, and web server. This is configured in the `mq-redux` build stage in `Dockerfile-server`.
|
||||
|
||||
@@ -16,5 +16,5 @@ docker run \
|
||||
--env LICENSE=accept \
|
||||
--env MQ_QMGR_NAME=QM1 \
|
||||
--detach \
|
||||
ibm-mqadvanced-server:9.3.1.1-amd64
|
||||
ibm-mqadvanced-server:9.3.2.0-amd64
|
||||
```
|
||||
|
||||
@@ -24,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=ibm-mqadvanced-server:9.3.1.1-amd64 make test-advancedserver
|
||||
MQ_IMAGE_ADVANCEDSERVER=ibm-mqadvanced-server:9.3.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:
|
||||
|
||||
@@ -112,3 +112,5 @@ For example, if you have an identity certificate you wish to add with the label
|
||||
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>')`
|
||||
|
||||
It must be noted that queue manager certificate with a Subject Distinguished Name (DN) same as it's Issuer certificate (CA) is not supported. Certificates must have a unique Subject Distinguished Name.
|
||||
@@ -1,4 +1,4 @@
|
||||
* © Copyright IBM Corporation 2019
|
||||
* © Copyright IBM Corporation 2019, 2022
|
||||
*
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@@ -16,4 +16,5 @@
|
||||
* Set the keystore location for the queue manager
|
||||
ALTER QMGR SSLKEYR('{{ .SSLKeyR }}')
|
||||
ALTER QMGR CERTLABL('{{ .CertificateLabel }}')
|
||||
ALTER QMGR SSLFIPS({{ .SSLFips }})
|
||||
REFRESH SECURITY(*) TYPE(SSL)
|
||||
|
||||
14
go.mod
14
go.mod
@@ -1,14 +1,24 @@
|
||||
module github.com/ibm-messaging/mq-container
|
||||
|
||||
go 1.15
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/genuinetools/amicontained v0.4.3
|
||||
github.com/ibm-messaging/mq-golang v2.0.0+incompatible
|
||||
github.com/prometheus/client_golang v1.11.1
|
||||
github.com/prometheus/client_model v0.2.0
|
||||
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 // indirect
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1
|
||||
software.sslmate.com/src/go-pkcs12 v0.0.0-20200830195227-52f69702a001
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.1 // indirect
|
||||
github.com/golang/protobuf v1.4.3 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
||||
github.com/prometheus/common v0.26.0 // indirect
|
||||
github.com/prometheus/procfs v0.6.0 // indirect
|
||||
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 // indirect
|
||||
google.golang.org/protobuf v1.26.0-rc.1 // indirect
|
||||
)
|
||||
|
||||
5
go.sum
5
go.sum
@@ -105,7 +105,6 @@ golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73r
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -120,16 +119,12 @@ golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
||||
@@ -6,6 +6,9 @@ NativeHALocalInstance:
|
||||
{{ if .CipherSpec }}
|
||||
CipherSpec={{ .CipherSpec }}
|
||||
{{- end }}
|
||||
{{ if .SSLFipsRequired }}
|
||||
SSLFipsRequired={{ .SSLFipsRequired }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
NativeHAInstance:
|
||||
Name={{ .NativeHAInstance0_Name }}
|
||||
|
||||
@@ -25,4 +25,4 @@ sudo apt-get update || :
|
||||
sudo apt-get install -y jq
|
||||
|
||||
go install golang.org/x/lint/golint@latest
|
||||
curl -sfL https://raw.githubusercontent.com/securego/gosec/master/install.sh | sh -s -- -b $GOPATH/bin 2.0.0 || echo "Gosec not installed. Platform may not be supported."
|
||||
curl -sfL https://raw.githubusercontent.com/securego/gosec/master/install.sh | sh -s -- -b $GOPATH/bin v2.14.0 || echo "Gosec not installed. Platform may not be supported."
|
||||
|
||||
@@ -36,12 +36,15 @@ func CopyFileMode(src, dest string, perm os.FileMode) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open %s for copy: %v", src, err)
|
||||
}
|
||||
// #nosec G307 - local to this function, pose no harm.
|
||||
defer in.Close()
|
||||
|
||||
// #nosec G304 - this func creates based on the input filemode.
|
||||
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)
|
||||
}
|
||||
// #nosec G307 - local to this function, pose no harm.
|
||||
defer out.Close()
|
||||
|
||||
_, err = io.Copy(out, in)
|
||||
|
||||
96
internal/fips/fips.go
Normal file
96
internal/fips/fips.go
Normal file
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2023
|
||||
|
||||
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 fips
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/ibm-messaging/mq-container/internal/command"
|
||||
"github.com/ibm-messaging/mq-container/pkg/logger"
|
||||
)
|
||||
|
||||
var (
|
||||
FIPSEnabledType int
|
||||
)
|
||||
|
||||
// FIPS has been turned off either because OS is not FIPS enabled or
|
||||
// MQ_ENABLE_FIPS environment variable is set to "false"
|
||||
const FIPS_ENABLED_OFF = 0
|
||||
|
||||
// FIPS is turned ON
|
||||
const FIPS_ENABLED_ON = 1
|
||||
|
||||
// FIPS enabled at operating system level
|
||||
const FIPS_ENABLED_PLATFORM = 1
|
||||
|
||||
// FIPS enabled via environment variable
|
||||
const FIPS_ENABLED_ENV_VAR = 2
|
||||
|
||||
// Get FIPS enabled type.
|
||||
func ProcessFIPSType(logs *logger.Logger) {
|
||||
// Run "sysctl crypto.fips_enabled" command to determine if FIPS has been enabled
|
||||
// on OS.
|
||||
FIPSEnabledType = FIPS_ENABLED_OFF
|
||||
|
||||
out, _, err := command.Run("sysctl", "crypto.fips_enabled")
|
||||
if err == nil {
|
||||
// Check the output of the command for expected output
|
||||
if strings.Contains(out, "crypto.fips_enabled = 1") {
|
||||
FIPSEnabledType = FIPS_ENABLED_PLATFORM
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we have been asked to override FIPS cryptography
|
||||
fipsOverride, fipsOverrideSet := os.LookupEnv("MQ_ENABLE_FIPS")
|
||||
if fipsOverrideSet {
|
||||
if strings.EqualFold(fipsOverride, "false") || strings.EqualFold(fipsOverride, "0") {
|
||||
FIPSEnabledType = FIPS_ENABLED_OFF
|
||||
} else if strings.EqualFold(fipsOverride, "true") || strings.EqualFold(fipsOverride, "1") {
|
||||
// This is the case where OS may or may not be FIPS compliant but we have been asked
|
||||
// to run MQ queue manager, web server and Native HA in FIPS mode. This case can also
|
||||
// be used when running docker tests. If FIPS is enabled on host, then don't modify
|
||||
// the original value.
|
||||
if FIPSEnabledType != FIPS_ENABLED_PLATFORM {
|
||||
FIPSEnabledType = FIPS_ENABLED_ENV_VAR
|
||||
}
|
||||
} else if strings.EqualFold(fipsOverride, "auto") {
|
||||
// This is the default case. Leave it to the OS default as determined above.
|
||||
} else {
|
||||
// We don't recognise the value specified. Log a warning and carry on.
|
||||
if logs != nil {
|
||||
logs.Printf("Invalid value '%s' was specified for MQ_ENABLE_FIPS. The value has been ignored.\n", fipsOverride)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func IsFIPSEnabled() bool {
|
||||
return FIPSEnabledType > FIPS_ENABLED_OFF
|
||||
}
|
||||
|
||||
// Log a message on the console to indicate FIPS certified
|
||||
// cryptography being used.
|
||||
func PostInit(log *logger.Logger) {
|
||||
message := "FIPS cryptography is not enabled."
|
||||
if FIPSEnabledType == FIPS_ENABLED_PLATFORM {
|
||||
message = "FIPS cryptography is enabled. FIPS cryptography setting on the host is 'true'."
|
||||
} else if FIPSEnabledType == FIPS_ENABLED_ENV_VAR {
|
||||
message = "FIPS cryptography is enabled. FIPS cryptography setting on the host is 'false'."
|
||||
}
|
||||
|
||||
log.Println(message)
|
||||
}
|
||||
65
internal/fips/fips_test.go
Normal file
65
internal/fips/fips_test.go
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2022
|
||||
|
||||
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 keystore contains code to create and update keystores
|
||||
package fips
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestEnableFIPSAuto(t *testing.T) {
|
||||
ProcessFIPSType(nil)
|
||||
// Test default "auto"
|
||||
fipsType := IsFIPSEnabled()
|
||||
if fipsType {
|
||||
t.Errorf("Expected FIPS OFF but got %v\n", fipsType)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnableFIPSTrue(t *testing.T) {
|
||||
// Test MQ_ENABLE_FIPS=true
|
||||
os.Setenv("MQ_ENABLE_FIPS", "true")
|
||||
fmt.Println(os.Getenv("MQ_ENABLE_FIPS"))
|
||||
ProcessFIPSType(nil)
|
||||
fipsType := IsFIPSEnabled()
|
||||
if !fipsType {
|
||||
t.Errorf("Expected FIPS ON but got %v\n", fipsType)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnableFIPSFalse(t *testing.T) {
|
||||
// Test MQ_ENABLE_FIPS=false
|
||||
os.Setenv("MQ_ENABLE_FIPS", "false")
|
||||
ProcessFIPSType(nil)
|
||||
fipsType := IsFIPSEnabled()
|
||||
if fipsType {
|
||||
t.Errorf("Expected FIPS OFF but got %v\n", fipsType)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestEnableFIPSInvalid(t *testing.T) {
|
||||
// Test MQ_ENABLE_FIPS with invalid value
|
||||
os.Setenv("MQ_ENABLE_FIPS", "falseOff")
|
||||
ProcessFIPSType(nil)
|
||||
fipsType := IsFIPSEnabled()
|
||||
if fipsType {
|
||||
t.Errorf("Expected FIPS OFF but got %v\n", fipsType)
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2020, 2021
|
||||
© Copyright IBM Corporation 2020, 2023
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -20,6 +20,7 @@ package ha
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/ibm-messaging/mq-container/internal/fips"
|
||||
"github.com/ibm-messaging/mq-container/internal/mqtemplate"
|
||||
"github.com/ibm-messaging/mq-container/internal/tls"
|
||||
"github.com/ibm-messaging/mq-container/pkg/logger"
|
||||
@@ -57,6 +58,13 @@ func ConfigureNativeHA(log *logger.Logger) error {
|
||||
if ok {
|
||||
templateMap["CipherSpec"] = cipherSpec
|
||||
}
|
||||
|
||||
// If FIPS is enabled, then set SSLFipsRequired to Yes
|
||||
if fips.IsFIPSEnabled() {
|
||||
templateMap["SSLFipsRequired"] = "Yes"
|
||||
} else {
|
||||
templateMap["SSLFipsRequired"] = "No"
|
||||
}
|
||||
}
|
||||
|
||||
err := mqtemplate.ProcessTemplateFile(templateFile, file, templateMap, log)
|
||||
|
||||
@@ -108,5 +108,6 @@ func (htpfile mapHtPasswd) updateHtPasswordFile(isTest bool) error {
|
||||
if isTest {
|
||||
file = "my.htpasswd"
|
||||
}
|
||||
// #nosec G306 - its a read by owner/s group, and pose no harm.
|
||||
return ioutil.WriteFile(file, htpfile.GetBytes(), 0660)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2018, 2022
|
||||
© Copyright IBM Corporation 2018, 2023
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -26,6 +26,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/ibm-messaging/mq-container/internal/command"
|
||||
"github.com/ibm-messaging/mq-container/internal/fips"
|
||||
)
|
||||
|
||||
// KeyStore describes information about a keystore file
|
||||
@@ -34,36 +35,46 @@ type KeyStore struct {
|
||||
Password string
|
||||
keyStoreType string
|
||||
command string
|
||||
fipsEnabled bool
|
||||
}
|
||||
|
||||
// NewJKSKeyStore creates a new Java Key Store, managed by the runmqckm command
|
||||
func NewJKSKeyStore(filename, password string) *KeyStore {
|
||||
return &KeyStore{
|
||||
keyStore := &KeyStore{
|
||||
Filename: filename,
|
||||
Password: password,
|
||||
keyStoreType: "jks",
|
||||
command: "/opt/mqm/bin/runmqckm",
|
||||
fipsEnabled: fips.IsFIPSEnabled(),
|
||||
}
|
||||
|
||||
return keyStore
|
||||
}
|
||||
|
||||
// NewCMSKeyStore creates a new MQ CMS Key Store, managed by the runmqakm command
|
||||
func NewCMSKeyStore(filename, password string) *KeyStore {
|
||||
return &KeyStore{
|
||||
keyStore := &KeyStore{
|
||||
Filename: filename,
|
||||
Password: password,
|
||||
keyStoreType: "cms",
|
||||
command: "/opt/mqm/bin/runmqakm",
|
||||
fipsEnabled: fips.IsFIPSEnabled(),
|
||||
}
|
||||
|
||||
return keyStore
|
||||
}
|
||||
|
||||
// NewPKCS12KeyStore creates a new PKCS12 Key Store, managed by the runmqakm command
|
||||
func NewPKCS12KeyStore(filename, password string) *KeyStore {
|
||||
return &KeyStore{
|
||||
keyStore := &KeyStore{
|
||||
Filename: filename,
|
||||
Password: password,
|
||||
keyStoreType: "p12",
|
||||
command: "/opt/mqm/bin/runmqakm",
|
||||
fipsEnabled: fips.IsFIPSEnabled(),
|
||||
}
|
||||
|
||||
return keyStore
|
||||
}
|
||||
|
||||
// Create a key store, if it doesn't already exist
|
||||
@@ -100,7 +111,7 @@ func (ks *KeyStore) Create() error {
|
||||
}
|
||||
|
||||
// Create the keystore now we're sure it doesn't exist
|
||||
out, _, err := command.Run(ks.command, "-keydb", "-create", "-type", ks.keyStoreType, "-db", ks.Filename, "-pw", ks.Password, "-stash")
|
||||
out, _, err := command.Run(ks.command, "-keydb", "-create", ks.getFipsEnabledFlag(), "-type", ks.keyStoreType, "-db", ks.Filename, "-pw", ks.Password, "-stash")
|
||||
if err != nil {
|
||||
return fmt.Errorf("error running \"%v -keydb -create\": %v %s", ks.command, err, out)
|
||||
}
|
||||
@@ -115,7 +126,7 @@ func (ks *KeyStore) CreateStash() error {
|
||||
_, err := os.Stat(stashFile)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
out, _, err := command.Run(ks.command, "-keydb", "-stashpw", "-type", ks.keyStoreType, "-db", ks.Filename, "-pw", ks.Password)
|
||||
out, _, err := command.Run(ks.command, "-keydb", ks.getFipsEnabledFlag(), "-stashpw", "-type", ks.keyStoreType, "-db", ks.Filename, "-pw", ks.Password)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error running \"%v -keydb -stashpw\": %v %s", ks.command, err, out)
|
||||
}
|
||||
@@ -127,7 +138,7 @@ func (ks *KeyStore) CreateStash() error {
|
||||
|
||||
// Import imports a certificate file in the keystore
|
||||
func (ks *KeyStore) Import(inputFile, password string) error {
|
||||
out, _, err := command.Run(ks.command, "-cert", "-import", "-file", inputFile, "-pw", password, "-target", ks.Filename, "-target_pw", ks.Password, "-target_type", ks.keyStoreType)
|
||||
out, _, err := command.Run(ks.command, "-cert", "-import", ks.getFipsEnabledFlag(), "-file", inputFile, "-pw", password, "-target", ks.Filename, "-target_pw", ks.Password, "-target_type", ks.keyStoreType)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error running \"%v -cert -import\": %v %s", ks.command, err, out)
|
||||
}
|
||||
@@ -136,7 +147,7 @@ func (ks *KeyStore) Import(inputFile, password string) error {
|
||||
|
||||
// CreateSelfSignedCertificate creates a self-signed certificate in the keystore
|
||||
func (ks *KeyStore) CreateSelfSignedCertificate(label, dn, hostname string) error {
|
||||
out, _, err := command.Run(ks.command, "-cert", "-create", "-db", ks.Filename, "-pw", ks.Password, "-label", label, "-dn", dn, "-san_dnsname", hostname, "-size 2048 -sig_alg sha256 -eku serverAuth")
|
||||
out, _, err := command.Run(ks.command, "-cert", "-create", ks.getFipsEnabledFlag(), "-db", ks.Filename, "-pw", ks.Password, "-label", label, "-dn", dn, "-san_dnsname", hostname, "-size 2048 -sig_alg sha256 -eku serverAuth")
|
||||
if err != nil {
|
||||
return fmt.Errorf("error running \"%v -cert -create\": %v %s", ks.command, err, out)
|
||||
}
|
||||
@@ -145,7 +156,7 @@ func (ks *KeyStore) CreateSelfSignedCertificate(label, dn, hostname string) erro
|
||||
|
||||
// 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)
|
||||
out, _, err := command.Run(ks.command, "-cert", "-add", ks.getFipsEnabledFlag(), "-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)
|
||||
}
|
||||
@@ -154,7 +165,7 @@ func (ks *KeyStore) Add(inputFile, label string) error {
|
||||
|
||||
// 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)
|
||||
out, _, err := command.Run(ks.command, "-cert", "-add", ks.getFipsEnabledFlag(), "-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)
|
||||
}
|
||||
@@ -163,7 +174,7 @@ func (ks *KeyStore) AddNoLabel(inputFile string) error {
|
||||
|
||||
// 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)
|
||||
out, _, err := command.Run(ks.command, "-cert", "-list", ks.getFipsEnabledFlag(), "-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)
|
||||
}
|
||||
@@ -171,8 +182,8 @@ func (ks *KeyStore) GetCertificateLabels() ([]string, error) {
|
||||
var labels []string
|
||||
for scanner.Scan() {
|
||||
s := scanner.Text()
|
||||
if strings.HasPrefix(s, "-") || strings.HasPrefix(s, "*-") || strings.HasPrefix(s, "!") {
|
||||
s := strings.TrimLeft(s, "-*!")
|
||||
if strings.HasPrefix(s, "-") || strings.HasPrefix(s, "*-") {
|
||||
s := strings.TrimLeft(s, "-*")
|
||||
labels = append(labels, strings.TrimSpace(s))
|
||||
}
|
||||
}
|
||||
@@ -207,7 +218,7 @@ func (ks *KeyStore) RenameCertificate(from, to string) error {
|
||||
|
||||
// ListAllCertificates 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)
|
||||
out, _, err := command.Run(ks.command, "-cert", "-list", ks.getFipsEnabledFlag(), "-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)
|
||||
}
|
||||
@@ -215,6 +226,8 @@ func (ks *KeyStore) ListAllCertificates() ([]string, error) {
|
||||
var labels []string
|
||||
for scanner.Scan() {
|
||||
s := scanner.Text()
|
||||
// Check for trusted certficates as well here as this method can
|
||||
// be called for trusted store as well.
|
||||
if strings.HasPrefix(s, "-") || strings.HasPrefix(s, "*-") || strings.HasPrefix(s, "!") {
|
||||
s := strings.TrimLeft(s, "-*!")
|
||||
labels = append(labels, strings.TrimSpace(s))
|
||||
@@ -226,3 +239,22 @@ func (ks *KeyStore) ListAllCertificates() ([]string, error) {
|
||||
}
|
||||
return labels, nil
|
||||
}
|
||||
|
||||
// Returns the FIPS flag. True if enabled else false
|
||||
func (ks *KeyStore) IsFIPSEnabled() bool {
|
||||
return ks.fipsEnabled
|
||||
}
|
||||
|
||||
// Returns -fips option if FIPS is enabled otherwise empty string. Return value is used
|
||||
// when running runmqakm/runmqckm commands.
|
||||
func (ks *KeyStore) getFipsEnabledFlag() string {
|
||||
var fipsEnabled string
|
||||
|
||||
if ks.fipsEnabled {
|
||||
fipsEnabled = "-fips"
|
||||
} else {
|
||||
fipsEnabled = ""
|
||||
}
|
||||
|
||||
return fipsEnabled
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2018, 2022
|
||||
© Copyright IBM Corporation 2018, 2023
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -35,6 +35,8 @@ const (
|
||||
|
||||
var (
|
||||
metricsEnabled = false
|
||||
// #nosec G112 - this code is changing soon to use https.
|
||||
// for now we will ignore the gosec.
|
||||
metricsServer = &http.Server{Addr: ":" + defaultPort}
|
||||
)
|
||||
|
||||
@@ -43,8 +45,8 @@ func GatherMetrics(qmName string, log *logger.Logger) {
|
||||
|
||||
// If running in standby mode - wait until the queue manager becomes active
|
||||
for {
|
||||
active, _ := ready.IsRunningAsActiveQM(context.Background(), qmName)
|
||||
if active {
|
||||
status, _ := ready.Status(context.Background(), qmName)
|
||||
if status.ActiveQM() {
|
||||
break
|
||||
}
|
||||
time.Sleep(requestTimeout * time.Second)
|
||||
|
||||
@@ -48,8 +48,10 @@ func ProcessTemplateFile(templateFile, destFile string, data interface{}, log *l
|
||||
return err
|
||||
}
|
||||
}
|
||||
// #nosec G302
|
||||
|
||||
// #nosec G302 G304 G306 - its a read by owner/s group, and pose no harm.
|
||||
f, err := os.OpenFile(destFile, os.O_CREATE|os.O_WRONLY, 0660)
|
||||
// #nosec G307 - local to this function, pose no harm.
|
||||
defer f.Close()
|
||||
err = t.Execute(f, data)
|
||||
if err != nil {
|
||||
|
||||
@@ -18,6 +18,7 @@ package mqversion
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/ibm-messaging/mq-container/internal/command"
|
||||
@@ -38,14 +39,59 @@ func Compare(checkVersion string) (int, error) {
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
// trim any suffix from MQ version x.x.x.x
|
||||
currentVersion = currentVersion[0:7]
|
||||
if currentVersion < checkVersion {
|
||||
return -1, nil
|
||||
} else if currentVersion == checkVersion {
|
||||
return 0, nil
|
||||
} else if currentVersion > checkVersion {
|
||||
return 1, nil
|
||||
|
||||
currentVRMF, err := parseVRMF(currentVersion)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return 0, fmt.Errorf("Failed to compare MQ versions")
|
||||
compareVRMF, err := parseVRMF(checkVersion)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to parse compare version: %w", err)
|
||||
}
|
||||
return currentVRMF.compare(*compareVRMF), nil
|
||||
}
|
||||
|
||||
type vrmf [4]int
|
||||
|
||||
func (v vrmf) String() string {
|
||||
return fmt.Sprintf("%d.%d.%d.%d", v[0], v[1], v[2], v[3])
|
||||
}
|
||||
|
||||
func (v vrmf) compare(to vrmf) int {
|
||||
for idx := 0; idx < 4; idx++ {
|
||||
if v[idx] < to[idx] {
|
||||
return -1
|
||||
}
|
||||
if v[idx] > to[idx] {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func parseVRMF(vrmfString string) (*vrmf, error) {
|
||||
versionParts := strings.Split(vrmfString, ".")
|
||||
if len(versionParts) != 4 {
|
||||
return nil, fmt.Errorf("incorrect number of parts to version string: expected 4, got %d", len(versionParts))
|
||||
}
|
||||
vmrfPartNames := []string{"version", "release", "minor", "fix"}
|
||||
parsed := vrmf{}
|
||||
for idx, value := range versionParts {
|
||||
partName := vmrfPartNames[idx]
|
||||
if value == "" {
|
||||
return nil, fmt.Errorf("empty %s found in VRMF", partName)
|
||||
}
|
||||
val, err := strconv.Atoi(value)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("non-numeric %s found in VRMF", partName)
|
||||
}
|
||||
if val < 0 {
|
||||
return nil, fmt.Errorf("negative %s found in VRMF", partName)
|
||||
}
|
||||
if idx == 0 && val == 0 {
|
||||
return nil, fmt.Errorf("zero value for version not allowed")
|
||||
}
|
||||
parsed[idx] = val
|
||||
}
|
||||
return &parsed, nil
|
||||
}
|
||||
|
||||
@@ -16,10 +16,13 @@ limitations under the License.
|
||||
|
||||
package mqversion
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCompareLower(t *testing.T) {
|
||||
checkVersion := "9.9.9.9"
|
||||
checkVersion := "99.99.99.99"
|
||||
mqVersionCheck, err := Compare(checkVersion)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to compare MQ versions: %v", err)
|
||||
@@ -53,3 +56,92 @@ func TestCompareEqual(t *testing.T) {
|
||||
t.Errorf("MQ version compare result failed. Expected 0, Got %v", mqVersionCheck)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVersionValid(t *testing.T) {
|
||||
checkVersion, err := Get()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get current MQ version: %v", err)
|
||||
}
|
||||
_, err = parseVRMF(checkVersion)
|
||||
if err != nil {
|
||||
t.Fatalf("Validation of MQ version failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidVRMF(t *testing.T) {
|
||||
validVRMFs := map[string]vrmf{
|
||||
"1.0.0.0": {1, 0, 0, 0},
|
||||
"10.0.0.0": {10, 0, 0, 0},
|
||||
"1.10.0.0": {1, 10, 0, 0},
|
||||
"1.0.10.0": {1, 0, 10, 0},
|
||||
"1.0.0.10": {1, 0, 0, 10},
|
||||
"999.998.997.996": {999, 998, 997, 996},
|
||||
}
|
||||
for test, expect := range validVRMFs {
|
||||
t.Run(test, func(t *testing.T) {
|
||||
parsed, err := parseVRMF(test)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpectedly failed to parse VRMF '%s': %s", test, err.Error())
|
||||
}
|
||||
if *parsed != expect {
|
||||
t.Fatalf("VRMF not parsed as expected. Expected '%v', got '%v'", parsed, expect)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestInvalidVRMF(t *testing.T) {
|
||||
invalidVRMFs := []string{
|
||||
"not-a-number",
|
||||
"9.8.7.string",
|
||||
"0.1.2.3",
|
||||
"1.0.0.-10",
|
||||
}
|
||||
for _, test := range invalidVRMFs {
|
||||
t.Run(test, func(t *testing.T) {
|
||||
parsed, err := parseVRMF(test)
|
||||
if err == nil {
|
||||
t.Fatalf("Expected error when parsing VRMF '%s', but got none. VRMF returned: %v", test, parsed)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompare(t *testing.T) {
|
||||
tests := []struct {
|
||||
current string
|
||||
compare string
|
||||
expect int
|
||||
}{
|
||||
{"1.0.0.1", "1.0.0.1", 0},
|
||||
{"1.0.0.1", "1.0.0.0", 1},
|
||||
{"1.0.0.1", "1.0.0.2", -1},
|
||||
{"9.9.9.9", "10.0.0.0", -1},
|
||||
{"9.9.9.9", "9.10.0.0", -1},
|
||||
{"9.9.9.9", "9.9.10.0", -1},
|
||||
{"9.9.9.9", "9.9.9.10", -1},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(fmt.Sprintf("%s-%s", test.current, test.compare), func(t *testing.T) {
|
||||
baseVRMF, err := parseVRMF(test.current)
|
||||
if err != nil {
|
||||
t.Fatalf("Could not parse base version '%s': %s", test.current, err.Error())
|
||||
}
|
||||
compareVRMF, err := parseVRMF(test.compare)
|
||||
if err != nil {
|
||||
t.Fatalf("Could not parse current version '%s': %s", test.current, err.Error())
|
||||
}
|
||||
result := baseVRMF.compare(*compareVRMF)
|
||||
if result != test.expect {
|
||||
t.Fatalf("Expected %d but got %d when comparing '%s' with '%s'", test.expect, result, test.current, test.compare)
|
||||
}
|
||||
if test.expect == 0 {
|
||||
return
|
||||
}
|
||||
resultReversed := compareVRMF.compare(*baseVRMF)
|
||||
if resultReversed != test.expect*-1 {
|
||||
t.Fatalf("Expected %d but got %d when comparing '%s' with '%s'", test.expect*-1, resultReversed, test.compare, test.current)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2018, 2022
|
||||
© Copyright IBM Corporation 2018, 2023
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -54,6 +54,7 @@ func Clear() error {
|
||||
// Set lets any subsequent calls to `CheckReady` know that the queue
|
||||
// manager has finished its configuration step
|
||||
func Set() error {
|
||||
// #nosec G306 - this gives permissions to owner/s group only.
|
||||
return ioutil.WriteFile(fileName, []byte("1"), 0770)
|
||||
}
|
||||
|
||||
@@ -67,28 +68,38 @@ func Check() (bool, error) {
|
||||
return exists, nil
|
||||
}
|
||||
|
||||
// IsRunningAsActiveQM returns true if the queue manager is running in active mode
|
||||
func IsRunningAsActiveQM(ctx context.Context, name string) (bool, error) {
|
||||
return isRunningQM(ctx, name, "(RUNNING)")
|
||||
}
|
||||
|
||||
// IsRunningAsStandbyQM returns true if the queue manager is running in standby mode
|
||||
func IsRunningAsStandbyQM(ctx context.Context, name string) (bool, error) {
|
||||
return isRunningQM(ctx, name, "(RUNNING AS STANDBY)")
|
||||
}
|
||||
|
||||
// IsRunningAsReplicaQM returns true if the queue manager is running in replica mode
|
||||
func IsRunningAsReplicaQM(ctx context.Context, name string) (bool, error) {
|
||||
return isRunningQM(ctx, name, "(REPLICA)")
|
||||
}
|
||||
|
||||
func isRunningQM(ctx context.Context, name string, status string) (bool, error) {
|
||||
// Status returns an enum representing the current running status of the queue manager
|
||||
func Status(ctx context.Context, name string) (QMStatus, error) {
|
||||
out, _, err := command.RunContext(ctx, "dspmq", "-n", "-m", name)
|
||||
if err != nil {
|
||||
return false, err
|
||||
return StatusUnknown, err
|
||||
}
|
||||
if strings.Contains(string(out), status) {
|
||||
return true, nil
|
||||
if strings.Contains(string(out), "(RUNNING)") {
|
||||
return StatusActiveQM, nil
|
||||
}
|
||||
return false, nil
|
||||
if strings.Contains(string(out), "(RUNNING AS STANDBY)") {
|
||||
return StatusStandbyQM, nil
|
||||
}
|
||||
if strings.Contains(string(out), "(REPLICA)") {
|
||||
return StatusStandbyQM, nil
|
||||
}
|
||||
return StatusUnknown, nil
|
||||
}
|
||||
|
||||
type QMStatus int
|
||||
|
||||
const (
|
||||
StatusUnknown QMStatus = iota
|
||||
StatusActiveQM
|
||||
StatusStandbyQM
|
||||
StatusReplicaQM
|
||||
)
|
||||
|
||||
// ActiveQM returns true if the queue manager is running in active mode
|
||||
func (s QMStatus) ActiveQM() bool { return s == StatusActiveQM }
|
||||
|
||||
// StandbyQM returns true if the queue manager is running in standby mode
|
||||
func (s QMStatus) StandbyQM() bool { return s == StatusStandbyQM }
|
||||
|
||||
// ReplicaQM returns true if the queue manager is running in replica mode
|
||||
func (s QMStatus) ReplicaQM() bool { return s == StatusReplicaQM }
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2019, 2022
|
||||
© Copyright IBM Corporation 2019, 2023
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -118,18 +118,25 @@ func ConfigureTLS(keyLabel string, cmsKeystore KeyStoreData, devMode bool, log *
|
||||
const mqsc string = "/etc/mqm/15-tls.mqsc"
|
||||
const mqscTemplate string = mqsc + ".tpl"
|
||||
sslKeyRing := ""
|
||||
var fipsEnabled = "NO"
|
||||
|
||||
// Don't set SSLKEYR if no keys or crts are not supplied
|
||||
// Key label will be blank if no certs were added during processing keys and certs.
|
||||
if cmsKeystore.Keystore != nil {
|
||||
// Key label will be blank if no private keys were added during processing keys and certs.
|
||||
if cmsKeystore.Keystore != nil && len(keyLabel) > 0 {
|
||||
certList, _ := cmsKeystore.Keystore.ListAllCertificates()
|
||||
if len(certList) > 0 {
|
||||
sslKeyRing = strings.TrimSuffix(cmsKeystore.Keystore.Filename, ".kdb")
|
||||
}
|
||||
|
||||
if cmsKeystore.Keystore.IsFIPSEnabled() {
|
||||
fipsEnabled = "YES"
|
||||
}
|
||||
}
|
||||
|
||||
err := mqtemplate.ProcessTemplateFile(mqscTemplate, mqsc, map[string]string{
|
||||
"SSLKeyR": sslKeyRing,
|
||||
"CertificateLabel": keyLabel,
|
||||
"SSLFips": fipsEnabled,
|
||||
}, log)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -248,11 +255,19 @@ func processKeys(tlsStore *TLSStore, keystoreDir string, keyDir string) (string,
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Validate certificates for duplicate Subject DNs
|
||||
if len(caCertificate) > 0 {
|
||||
errCertValid := validateCertificates(publicCertificate, caCertificate)
|
||||
if errCertValid != nil {
|
||||
return "", errCertValid
|
||||
}
|
||||
}
|
||||
// Create a new PKCS#12 Keystore - containing private key, public certificate & optional CA certificate
|
||||
file, err := pkcs.Encode(rand.Reader, privateKey, publicCertificate, caCertificate, tlsStore.Keystore.Password)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Failed to encode PKCS#12 Keystore %s: %v", keySet.Name()+".p12", err)
|
||||
}
|
||||
// #nosec G306 - this gives permissions to owner/s group only.
|
||||
err = ioutil.WriteFile(filepath.Join(keystoreDir, keySet.Name()+".p12"), file, 0644)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Failed to write PKCS#12 Keystore %s: %v", filepath.Join(keystoreDir, keySet.Name()+".p12"), err)
|
||||
@@ -556,6 +571,7 @@ func generateRandomPassword() string {
|
||||
validcharArray := []byte(validChars)
|
||||
password := ""
|
||||
for i := 0; i < 12; i++ {
|
||||
// #nosec G404 - this is only for internal keystore and using math/rand pose no harm.
|
||||
password = password + string(validcharArray[pwr.Intn(len(validcharArray))])
|
||||
}
|
||||
|
||||
@@ -600,10 +616,13 @@ func getCertificateFingerprint(block *pem.Block) (string, error) {
|
||||
|
||||
// writeCertificatesToFile writes a list of certificates to a file
|
||||
func writeCertificatesToFile(file string, certificates []*pem.Block) error {
|
||||
|
||||
// #nosec G304 - this is a temporary pem file to write certs.
|
||||
f, err := os.Create(file)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to create file %s: %v", file, err)
|
||||
}
|
||||
// #nosec G307 - local to this function, pose no harm.
|
||||
defer f.Close()
|
||||
|
||||
w := bufio.NewWriter(f)
|
||||
@@ -631,7 +650,7 @@ func haveKeysAndCerts(keyDir string) bool {
|
||||
// Do a listing of the subdirectory and then search for .key and .cert files
|
||||
keys, _ := ioutil.ReadDir(filepath.Join(keyDir, fileInfo.Name()))
|
||||
for _, key := range keys {
|
||||
if strings.Contains(key.Name(), ".key") || strings.Contains(key.Name(), ".crt") {
|
||||
if strings.HasSuffix(key.Name(), ".key") || strings.HasSuffix(key.Name(), ".crt") {
|
||||
// We found at least one key/crt file.
|
||||
return true
|
||||
}
|
||||
@@ -640,3 +659,19 @@ func haveKeysAndCerts(keyDir string) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Iterate through the certificates to ensure there are no two certificates with same Subject DN.
|
||||
// GSKit does not allow two certificates with same Subject DN/Friendly Names
|
||||
func validateCertificates(personalCert *x509.Certificate, caCertificates []*x509.Certificate) error {
|
||||
// Check if we have been asked to override certificate validation by setting
|
||||
// MQ_ENABLE_CERT_VALIDATION to false
|
||||
enableValidation, enableValidationSet := os.LookupEnv("MQ_ENABLE_CERT_VALIDATION")
|
||||
if !enableValidationSet || (enableValidationSet && !strings.EqualFold(strings.Trim(enableValidation, ""), "false")) {
|
||||
for _, caCert := range caCertificates {
|
||||
if strings.EqualFold(personalCert.Subject.String(), caCert.Subject.String()) {
|
||||
return fmt.Errorf("Error: The Subject DN of the Issuer Certificate and the Queue Manager are same")
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -77,7 +77,6 @@ func ConfigureWebKeystore(p12Truststore KeyStoreData, webKeystore string) (strin
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Failed to generate certificate in Web Keystore %s with DN of 'CN=%s': %v", webKeystoreFile, genHostName, err)
|
||||
}
|
||||
|
||||
} else {
|
||||
// Check Web Keystore already exists
|
||||
_, err := os.Stat(webKeystoreFile)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2017, 2020
|
||||
© Copyright IBM Corporation 2017, 2023
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -102,5 +102,10 @@ func LogContainerDetails(log *logger.Logger) error {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if os.Getenv("MQ_LOGGING_CONSOLE_FORMAT") == "" && os.Getenv("LOG_FORMAT") != "" {
|
||||
log.Println("Environment variable LOG_FORMAT is deprecated. Use MQ_LOGGING_CONSOLE_FORMAT instead.")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/bash
|
||||
# -*- mode: sh -*-
|
||||
# © Copyright IBM Corporation 2015, 2022
|
||||
# © Copyright IBM Corporation 2015, 2023
|
||||
#
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@@ -21,43 +21,8 @@ set -ex
|
||||
test -f /usr/bin/rpm && RPM=true || RPM=false
|
||||
test -f /usr/bin/apt-get && UBUNTU=true || UBUNTU=false
|
||||
|
||||
# Download and extract the MQ unzippable server
|
||||
DIR_TMP=/tmp/mq
|
||||
mkdir -p ${DIR_TMP}
|
||||
cd ${DIR_TMP}
|
||||
curl --fail --location $MQ_URL | tar --extract --gunzip
|
||||
ls -la ${DIR_TMP}
|
||||
|
||||
# Generate MQ package in INSTALLATION_DIR
|
||||
export genmqpkg_inc32=0
|
||||
export genmqpkg_incadm=1
|
||||
export genmqpkg_incamqp=0
|
||||
export genmqpkg_incams=1
|
||||
export genmqpkg_inccbl=0
|
||||
export genmqpkg_inccics=0
|
||||
export genmqpkg_inccpp=0
|
||||
export genmqpkg_incdnet=0
|
||||
export genmqpkg_incjava=1
|
||||
export genmqpkg_incjre=1
|
||||
export genmqpkg_incman=0
|
||||
export genmqpkg_incmqbc=0
|
||||
export genmqpkg_incmqft=0
|
||||
export genmqpkg_incmqsf=0
|
||||
export genmqpkg_incmqxr=0
|
||||
export genmqpkg_incnls=1
|
||||
export genmqpkg_incras=1
|
||||
export genmqpkg_incsamp=1
|
||||
export genmqpkg_incsdk=0
|
||||
export genmqpkg_inctls=1
|
||||
export genmqpkg_incunthrd=0
|
||||
export genmqpkg_incweb=1
|
||||
export INSTALLATION_DIR=/opt/mqm
|
||||
${DIR_TMP}/bin/genmqpkg.sh -b ${INSTALLATION_DIR}
|
||||
ls -la ${INSTALLATION_DIR}
|
||||
rm -rf ${DIR_TMP}
|
||||
|
||||
# Accept the MQ license
|
||||
${INSTALLATION_DIR}/bin/mqlicense -accept
|
||||
/opt/mqm/bin/mqlicense -accept
|
||||
|
||||
# Optional: Update the command prompt with the MQ version
|
||||
$UBUNTU && echo "mq:$(dspmqver -b -f 2)" > /etc/debian_chroot
|
||||
@@ -2,6 +2,6 @@
|
||||
|
||||
# SOURCE_BRANCH is the repository branch name for this release stream.
|
||||
# It should be updated when a new release fork is created but not for testing of personal builds or pre-fork updates.
|
||||
SOURCE_BRANCH ?= v9.3.1
|
||||
SOURCE_BRANCH ?= private-master
|
||||
|
||||
###########################################################################################################################################################
|
||||
|
||||
@@ -23,6 +23,8 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
"crypto/tls"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/network"
|
||||
@@ -58,10 +60,10 @@ func TestDevGoldenPath(t *testing.T) {
|
||||
runJMSTests(t, cli, id, false, "app", defaultAppPasswordOS, "false", "")
|
||||
})
|
||||
t.Run("REST admin", func(t *testing.T) {
|
||||
testRESTAdmin(t, cli, id, insecureTLSConfig)
|
||||
testRESTAdmin(t, cli, id, insecureTLSConfig, "")
|
||||
})
|
||||
t.Run("REST messaging", func(t *testing.T) {
|
||||
testRESTMessaging(t, cli, id, insecureTLSConfig, qm, "app", defaultAppPasswordWeb)
|
||||
testRESTMessaging(t, cli, id, insecureTLSConfig, qm, "app", defaultAppPasswordWeb, "")
|
||||
})
|
||||
// Stop the container cleanly
|
||||
stopContainer(t, cli, id)
|
||||
@@ -123,10 +125,10 @@ func TestDevSecure(t *testing.T) {
|
||||
runJMSTests(t, cli, ctr.ID, true, "app", appPassword, "false", "TLS_RSA_WITH_AES_256_CBC_SHA256")
|
||||
})
|
||||
t.Run("REST admin", func(t *testing.T) {
|
||||
testRESTAdmin(t, cli, ctr.ID, insecureTLSConfig)
|
||||
testRESTAdmin(t, cli, ctr.ID, insecureTLSConfig, "")
|
||||
})
|
||||
t.Run("REST messaging", func(t *testing.T) {
|
||||
testRESTMessaging(t, cli, ctr.ID, insecureTLSConfig, qm, "app", appPassword)
|
||||
testRESTMessaging(t, cli, ctr.ID, insecureTLSConfig, qm, "app", appPassword, "")
|
||||
})
|
||||
|
||||
// Stop the container cleanly
|
||||
@@ -204,18 +206,35 @@ func TestSSLKEYRBlank(t *testing.T) {
|
||||
containerConfig := container.Config{
|
||||
Env: []string{
|
||||
"LICENSE=accept",
|
||||
"MQ_QMGR_NAME=qm1",
|
||||
"MQ_QMGR_NAME=QM1",
|
||||
"MQ_ENABLE_EMBEDDED_WEB_SERVER=false",
|
||||
},
|
||||
}
|
||||
id := runContainerWithPorts(t, cli, &containerConfig, []int{9443})
|
||||
defer cleanContainer(t, cli, id)
|
||||
waitForReady(t, cli, id)
|
||||
|
||||
// execute runmqsc to display qmgr SSLKEYR and CERTLABL attibutes.
|
||||
// Search the console output for exepcted values
|
||||
_, sslkeyROutput := execContainer(t, cli, id, "", []string{"bash", "-c", "echo 'DISPLAY QMGR SSLKEYR CERTLABL' | runmqsc"})
|
||||
if !strings.Contains(sslkeyROutput, "SSLKEYR( )") && !strings.Contains(sslkeyROutput, "CERTLABL( )") {
|
||||
t.Errorf("Expected SSLKEYR to be blank but it is not; got \"%v\"", sslkeyROutput)
|
||||
if !strings.Contains(sslkeyROutput, "SSLKEYR( )") || !strings.Contains(sslkeyROutput, "CERTLABL( )") {
|
||||
// Although queue manager is ready, it may be that MQSC scripts have not been applied yet.
|
||||
// Hence wait for a second and retry few times before giving up.
|
||||
waitCount := 30
|
||||
var i int
|
||||
for i = 0; i < waitCount; i++ {
|
||||
time.Sleep(1 * time.Second)
|
||||
_, sslkeyROutput = execContainer(t, cli, id, "", []string{"bash", "-c", "echo 'DISPLAY QMGR SSLKEYR CERTLABL' | runmqsc"})
|
||||
if strings.Contains(sslkeyROutput, "SSLKEYR( )") && strings.Contains(sslkeyROutput, "CERTLABL( )") {
|
||||
break
|
||||
}
|
||||
}
|
||||
// Failed to get expected output? dump the contents of mqsc files.
|
||||
if i == waitCount {
|
||||
_, tls15mqsc := execContainer(t, cli, id, "", []string{"cat", "/etc/mqm/15-tls.mqsc"})
|
||||
_, autoMQSC := execContainer(t, cli, id, "", []string{"cat", "/mnt/mqm/data/qmgrs/QM1/autocfg/cached.mqsc"})
|
||||
t.Errorf("Expected SSLKEYR to be blank but it is not; got \"%v\"\n AutoConfig MQSC file contents %v\n 15-tls: %v", sslkeyROutput, autoMQSC, tls15mqsc)
|
||||
}
|
||||
}
|
||||
|
||||
// Stop the container cleanly
|
||||
@@ -254,11 +273,28 @@ func TestSSLKEYRWithSuppliedKeyAndCert(t *testing.T) {
|
||||
defer cleanContainer(t, cli, ctr.ID)
|
||||
startContainer(t, cli, ctr.ID)
|
||||
waitForReady(t, cli, ctr.ID)
|
||||
|
||||
// execute runmqsc to display qmgr SSLKEYR and CERTLABL attibutes.
|
||||
// Search the console output for exepcted values
|
||||
_, sslkeyROutput := execContainer(t, cli, ctr.ID, "", []string{"bash", "-c", "echo 'DISPLAY QMGR SSLKEYR CERTLABL' | runmqsc"})
|
||||
if !strings.Contains(sslkeyROutput, "SSLKEYR(/run/runmqserver/tls/key)") && !strings.Contains(sslkeyROutput, "CERTLABL(default)") {
|
||||
t.Errorf("Expected SSLKEYR to be '/run/runmqserver/tls/key' but it is not; got \"%v\"", sslkeyROutput)
|
||||
if !strings.Contains(sslkeyROutput, "SSLKEYR(/run/runmqserver/tls/key)") || !strings.Contains(sslkeyROutput, "CERTLABL(default)") {
|
||||
// Although queue manager is ready, it may be that MQSC scripts have not been applied yet.
|
||||
// Hence wait for a second and retry few times before giving up.
|
||||
waitCount := 30
|
||||
var i int
|
||||
for i = 0; i < waitCount; i++ {
|
||||
time.Sleep(1 * time.Second)
|
||||
_, sslkeyROutput = execContainer(t, cli, ctr.ID, "", []string{"bash", "-c", "echo 'DISPLAY QMGR SSLKEYR CERTLABL' | runmqsc"})
|
||||
if strings.Contains(sslkeyROutput, "SSLKEYR(/run/runmqserver/tls/key)") && strings.Contains(sslkeyROutput, "CERTLABL(default)") {
|
||||
break
|
||||
}
|
||||
}
|
||||
// Failed to get expected output? dump the contents of mqsc files.
|
||||
if i == waitCount {
|
||||
_, tls15mqsc := execContainer(t, cli, ctr.ID, "", []string{"cat", "/etc/mqm/15-tls.mqsc"})
|
||||
_, autoMQSC := execContainer(t, cli, ctr.ID, "", []string{"cat", "/mnt/mqm/data/qmgrs/QM1/autocfg/cached.mqsc"})
|
||||
t.Errorf("Expected SSLKEYR to be '/run/runmqserver/tls/key' but it is not; got \"%v\" \n AutoConfig MQSC file contents %v\n 15-tls: %v", sslkeyROutput, autoMQSC, tls15mqsc)
|
||||
}
|
||||
}
|
||||
|
||||
// Stop the container cleanly
|
||||
@@ -308,12 +344,439 @@ func TestSSLKEYRWithCACert(t *testing.T) {
|
||||
// execute runmqsc to display qmgr SSLKEYR and CERTLABL attibutes.
|
||||
// Search the console output for exepcted values
|
||||
_, sslkeyROutput := execContainer(t, cli, ctr.ID, "", []string{"bash", "-c", "echo 'DISPLAY QMGR SSLKEYR CERTLABL' | runmqsc"})
|
||||
|
||||
if !strings.Contains(sslkeyROutput, "SSLKEYR(/run/runmqserver/tls/key)") {
|
||||
t.Errorf("Expected SSLKEYR to be '/run/runmqserver/tls/key' but it is not; got \"%v\"", sslkeyROutput)
|
||||
// Although queue manager is ready, it may be that MQSC scripts have not been applied yet.
|
||||
// Hence wait for a second and retry few times before giving up.
|
||||
waitCount := 30
|
||||
var i int
|
||||
for i = 0; i < waitCount; i++ {
|
||||
time.Sleep(1 * time.Second)
|
||||
_, sslkeyROutput = execContainer(t, cli, ctr.ID, "", []string{"bash", "-c", "echo 'DISPLAY QMGR SSLKEYR CERTLABL' | runmqsc"})
|
||||
if strings.Contains(sslkeyROutput, "SSLKEYR(/run/runmqserver/tls/key)") {
|
||||
break
|
||||
}
|
||||
}
|
||||
// Failed to get expected output? dump the contents of mqsc files.
|
||||
if i == waitCount {
|
||||
_, tls15mqsc := execContainer(t, cli, ctr.ID, "", []string{"cat", "/etc/mqm/15-tls.mqsc"})
|
||||
_, autoMQSC := execContainer(t, cli, ctr.ID, "", []string{"cat", "/mnt/mqm/data/qmgrs/QM1/autocfg/cached.mqsc"})
|
||||
t.Errorf("Expected SSLKEYR to be '/run/runmqserver/tls/key' but it is not; got \"%v\"\n AutoConfig MQSC file contents %v\n 15-tls: %v", sslkeyROutput, autoMQSC, tls15mqsc)
|
||||
}
|
||||
}
|
||||
|
||||
if !strings.Contains(sslkeyROutput, "CERTLABL(QM1CA)") {
|
||||
t.Errorf("Expected CERTLABL to be 'QM1CA' but it is not; got \"%v\"", sslkeyROutput)
|
||||
_, autoMQSC := execContainer(t, cli, ctr.ID, "", []string{"cat", "/etc/mqm/15-tls.mqsc"})
|
||||
t.Errorf("Expected CERTLABL to be 'QM1CA' but it is not; got \"%v\" \n MQSC File contents %v", sslkeyROutput, autoMQSC)
|
||||
}
|
||||
|
||||
// Stop the container cleanly
|
||||
stopContainer(t, cli, ctr.ID)
|
||||
}
|
||||
|
||||
// Verifies SSLFIPS is set to NO if MQ_ENABLE_FIPS=false
|
||||
func TestSSLFIPSNO(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cli, err := client.NewClientWithOpts(client.FromEnv)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
containerConfig := container.Config{
|
||||
Env: []string{
|
||||
"LICENSE=accept",
|
||||
"MQ_QMGR_NAME=QM1",
|
||||
"MQ_ENABLE_EMBEDDED_WEB_SERVER=false",
|
||||
"MQ_ENABLE_FIPS=false",
|
||||
},
|
||||
Image: imageName(),
|
||||
}
|
||||
hostConfig := container.HostConfig{
|
||||
Binds: []string{
|
||||
coverageBind(t),
|
||||
tlsDir(t, false) + ":/etc/mqm/pki/keys/default",
|
||||
},
|
||||
}
|
||||
networkingConfig := network.NetworkingConfig{}
|
||||
ctr, err := cli.ContainerCreate(context.Background(), &containerConfig, &hostConfig, &networkingConfig, t.Name())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer cleanContainer(t, cli, ctr.ID)
|
||||
startContainer(t, cli, ctr.ID)
|
||||
waitForReady(t, cli, ctr.ID)
|
||||
|
||||
// execute runmqsc to display qmgr SSLKEYR, SSLFIPS and CERTLABL attibutes.
|
||||
// Search the console output for exepcted values
|
||||
_, sslFIPSOutput := execContainer(t, cli, ctr.ID, "", []string{"bash", "-c", "echo 'DISPLAY QMGR SSLKEYR CERTLABL SSLFIPS' | runmqsc"})
|
||||
if !strings.Contains(sslFIPSOutput, "SSLKEYR(/run/runmqserver/tls/key)") {
|
||||
t.Errorf("Expected SSLKEYR to be '/run/runmqserver/tls/key' but it is not; got \"%v\"", sslFIPSOutput)
|
||||
}
|
||||
if !strings.Contains(sslFIPSOutput, "CERTLABL(default)") {
|
||||
t.Errorf("Expected CERTLABL to be 'default' but it is not; got \"%v\"", sslFIPSOutput)
|
||||
}
|
||||
|
||||
if !strings.Contains(sslFIPSOutput, "SSLFIPS(NO)") {
|
||||
t.Errorf("Expected SSLFIPS to be 'NO' but it is not; got \"%v\"", sslFIPSOutput)
|
||||
}
|
||||
|
||||
// Stop the container cleanly
|
||||
stopContainer(t, cli, ctr.ID)
|
||||
}
|
||||
|
||||
// Verifies SSLFIPS is set to YES if certificates for queue manager
|
||||
// are supplied and MQ_ENABLE_FIPS=true
|
||||
func TestSSLFIPSYES(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cli, err := client.NewClientWithOpts(client.FromEnv)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
appPassword := "differentPassw0rd"
|
||||
containerConfig := container.Config{
|
||||
Env: []string{
|
||||
"LICENSE=accept",
|
||||
"MQ_APP_PASSWORD=" + appPassword,
|
||||
"MQ_QMGR_NAME=QM1",
|
||||
"MQ_ENABLE_EMBEDDED_WEB_SERVER=false",
|
||||
"MQ_ENABLE_FIPS=true",
|
||||
},
|
||||
Image: imageName(),
|
||||
}
|
||||
hostConfig := container.HostConfig{
|
||||
Binds: []string{
|
||||
coverageBind(t),
|
||||
tlsDir(t, false) + ":/etc/mqm/pki/keys/default",
|
||||
},
|
||||
}
|
||||
networkingConfig := network.NetworkingConfig{}
|
||||
ctr, err := cli.ContainerCreate(context.Background(), &containerConfig, &hostConfig, &networkingConfig, t.Name())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer cleanContainer(t, cli, ctr.ID)
|
||||
startContainer(t, cli, ctr.ID)
|
||||
waitForReady(t, cli, ctr.ID)
|
||||
|
||||
// Check for expected message on container log
|
||||
logs := inspectLogs(t, cli, ctr.ID)
|
||||
if !strings.Contains(logs, "FIPS cryptography is enabled.") {
|
||||
t.Errorf("Expected 'FIPS cryptography is enabled.' but got %v\n", logs)
|
||||
}
|
||||
|
||||
// execute runmqsc to display qmgr SSLKEYR, SSLFIPS and CERTLABL attibutes.
|
||||
// Search the console output for exepcted values
|
||||
_, sslFIPSOutput := execContainer(t, cli, ctr.ID, "", []string{"bash", "-c", "echo 'DISPLAY QMGR SSLKEYR CERTLABL SSLFIPS' | runmqsc"})
|
||||
if !strings.Contains(sslFIPSOutput, "SSLKEYR(/run/runmqserver/tls/key)") {
|
||||
t.Errorf("Expected SSLKEYR to be '/run/runmqserver/tls/key' but it is not; got \"%v\"", sslFIPSOutput)
|
||||
}
|
||||
if !strings.Contains(sslFIPSOutput, "CERTLABL(default)") {
|
||||
t.Errorf("Expected CERTLABL to be 'default' but it is not; got \"%v\"", sslFIPSOutput)
|
||||
}
|
||||
|
||||
if !strings.Contains(sslFIPSOutput, "SSLFIPS(YES)") {
|
||||
t.Errorf("Expected SSLFIPS to be 'YES' but it is not; got \"%v\"", sslFIPSOutput)
|
||||
}
|
||||
|
||||
t.Run("JMS", func(t *testing.T) {
|
||||
// Run the JMS tests, with no password specified
|
||||
runJMSTests(t, cli, ctr.ID, true, "app", appPassword, "false", "TLS_RSA_WITH_AES_256_CBC_SHA256")
|
||||
})
|
||||
|
||||
// Stop the container cleanly
|
||||
stopContainer(t, cli, ctr.ID)
|
||||
}
|
||||
|
||||
// TestDevSecureFIPSYESWeb verifies if the MQ Web Server is running in FIPS mode
|
||||
func TestDevSecureFIPSTrueWeb(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cli, err := client.NewClientWithOpts(client.FromEnv)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
const tlsPassPhrase string = "passw0rd"
|
||||
qm := "qm1"
|
||||
appPassword := "differentPassw0rd"
|
||||
containerConfig := container.Config{
|
||||
Env: []string{
|
||||
"LICENSE=accept",
|
||||
"MQ_QMGR_NAME=" + qm,
|
||||
"MQ_APP_PASSWORD=" + appPassword,
|
||||
"DEBUG=1",
|
||||
"WLP_LOGGING_MESSAGE_FORMAT=JSON",
|
||||
"MQ_ENABLE_EMBEDDED_WEB_SERVER_LOG=true",
|
||||
"MQ_ENABLE_FIPS=true",
|
||||
},
|
||||
Image: imageName(),
|
||||
}
|
||||
hostConfig := container.HostConfig{
|
||||
Binds: []string{
|
||||
coverageBind(t),
|
||||
tlsDir(t, false) + ":/etc/mqm/pki/keys/default",
|
||||
tlsDir(t, false) + ":/etc/mqm/pki/trust/default",
|
||||
},
|
||||
// Assign a random port for the web server on the host
|
||||
// TODO: Don't do this for all tests
|
||||
PortBindings: nat.PortMap{
|
||||
"9443/tcp": []nat.PortBinding{
|
||||
{
|
||||
HostIP: "0.0.0.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
networkingConfig := network.NetworkingConfig{}
|
||||
ctr, err := cli.ContainerCreate(context.Background(), &containerConfig, &hostConfig, &networkingConfig, t.Name())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer cleanContainer(t, cli, ctr.ID)
|
||||
|
||||
startContainer(t, cli, ctr.ID)
|
||||
waitForReady(t, cli, ctr.ID)
|
||||
cert := filepath.Join(tlsDir(t, true), "server.crt")
|
||||
waitForWebReady(t, cli, ctr.ID, createTLSConfig(t, cert, tlsPassPhrase))
|
||||
|
||||
// Create a TLS Config with a cipher to use when connecting over HTTPS
|
||||
var secureTLSConfig *tls.Config = createTLSConfigWithCipher(t, cert, tlsPassPhrase, []uint16{tls.TLS_RSA_WITH_AES_256_GCM_SHA384})
|
||||
// Put a message to queue
|
||||
t.Run("REST messaging", func(t *testing.T) {
|
||||
testRESTMessaging(t, cli, ctr.ID, secureTLSConfig, qm, "app", appPassword, "")
|
||||
})
|
||||
|
||||
// Create a TLS Config with a non-FIPS cipher to use when connecting over HTTPS
|
||||
var secureNonFIPSCipherConfig *tls.Config = createTLSConfigWithCipher(t, cert, tlsPassPhrase, []uint16{tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA})
|
||||
// Put a message to queue - the attempt to put message will fail with a EOF return message.
|
||||
t.Run("REST messaging", func(t *testing.T) {
|
||||
testRESTMessaging(t, cli, ctr.ID, secureNonFIPSCipherConfig, qm, "app", appPassword, "EOF")
|
||||
})
|
||||
|
||||
// Stop the container cleanly
|
||||
stopContainer(t, cli, ctr.ID)
|
||||
}
|
||||
|
||||
// TestDevSecureNOFIPSWeb verifies if the MQ Web Server is not running in FIPS mode
|
||||
func TestDevSecureFalseFIPSWeb(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cli, err := client.NewClientWithOpts(client.FromEnv)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
const tlsPassPhrase string = "passw0rd"
|
||||
qm := "qm1"
|
||||
appPassword := "differentPassw0rd"
|
||||
containerConfig := container.Config{
|
||||
Env: []string{
|
||||
"LICENSE=accept",
|
||||
"MQ_QMGR_NAME=" + qm,
|
||||
"MQ_APP_PASSWORD=" + appPassword,
|
||||
"DEBUG=1",
|
||||
"WLP_LOGGING_MESSAGE_FORMAT=JSON",
|
||||
"MQ_ENABLE_EMBEDDED_WEB_SERVER_LOG=true",
|
||||
"MQ_ENABLE_FIPS=false",
|
||||
},
|
||||
Image: imageName(),
|
||||
}
|
||||
hostConfig := container.HostConfig{
|
||||
Binds: []string{
|
||||
coverageBind(t),
|
||||
tlsDir(t, false) + ":/etc/mqm/pki/keys/default",
|
||||
tlsDir(t, false) + ":/etc/mqm/pki/trust/default",
|
||||
},
|
||||
// Assign a random port for the web server on the host
|
||||
PortBindings: nat.PortMap{
|
||||
"9443/tcp": []nat.PortBinding{
|
||||
{
|
||||
HostIP: "0.0.0.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
networkingConfig := network.NetworkingConfig{}
|
||||
ctr, err := cli.ContainerCreate(context.Background(), &containerConfig, &hostConfig, &networkingConfig, t.Name())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer cleanContainer(t, cli, ctr.ID)
|
||||
startContainer(t, cli, ctr.ID)
|
||||
waitForReady(t, cli, ctr.ID)
|
||||
|
||||
cert := filepath.Join(tlsDir(t, true), "server.crt")
|
||||
waitForWebReady(t, cli, ctr.ID, createTLSConfig(t, cert, tlsPassPhrase))
|
||||
|
||||
// As FIPS is not enabled, the MQ WebServer (actually Java) will choose a JSSE provider from the list
|
||||
// specified in java.security file. We will need to enable java.net.debug and then parse the web server
|
||||
// logs to check what JJSE provider is being used. Hence just check the jvm.options file does not contain
|
||||
// -Dcom.ibm.jsse2.usefipsprovider line.
|
||||
_, jvmOptionsOutput := execContainer(t, cli, ctr.ID, "", []string{"bash", "-c", "cat /var/mqm/web/installations/Installation1/servers/mqweb/configDropins/defaults/jvm.options"})
|
||||
if strings.Contains(jvmOptionsOutput, "-Dcom.ibm.jsse2.usefipsprovider") {
|
||||
t.Errorf("Did not expect -Dcom.ibm.jsse2.usefipsprovider but it is not; got \"%v\"", jvmOptionsOutput)
|
||||
}
|
||||
|
||||
// Just do a HTTPS GET as well to query installation details.
|
||||
var secureTLSConfig *tls.Config = createTLSConfigWithCipher(t, cert, tlsPassPhrase, []uint16{tls.TLS_RSA_WITH_AES_256_GCM_SHA384})
|
||||
t.Run("REST admin", func(t *testing.T) {
|
||||
testRESTAdmin(t, cli, ctr.ID, secureTLSConfig, "")
|
||||
})
|
||||
|
||||
// Stop the container cleanly
|
||||
stopContainer(t, cli, ctr.ID)
|
||||
}
|
||||
|
||||
// Verify SSLFIPS is set to NO if no certificates were supplied
|
||||
func TestSSLFIPSTrueNoCerts(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cli, err := client.NewClientWithOpts(client.FromEnv)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
appPassword := "differentPassw0rd"
|
||||
containerConfig := container.Config{
|
||||
Env: []string{
|
||||
"LICENSE=accept",
|
||||
"MQ_APP_PASSWORD=" + appPassword,
|
||||
"MQ_QMGR_NAME=QM1",
|
||||
"MQ_ENABLE_EMBEDDED_WEB_SERVER=false",
|
||||
"MQ_ENABLE_FIPS=true",
|
||||
},
|
||||
Image: imageName(),
|
||||
}
|
||||
hostConfig := container.HostConfig{
|
||||
Binds: []string{
|
||||
coverageBind(t),
|
||||
},
|
||||
}
|
||||
networkingConfig := network.NetworkingConfig{}
|
||||
ctr, err := cli.ContainerCreate(context.Background(), &containerConfig, &hostConfig, &networkingConfig, t.Name())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer cleanContainer(t, cli, ctr.ID)
|
||||
startContainer(t, cli, ctr.ID)
|
||||
waitForReady(t, cli, ctr.ID)
|
||||
|
||||
// execute runmqsc to display qmgr SSLKEYR, SSLFIPS and CERTLABL attibutes.
|
||||
// Search the console output for exepcted values
|
||||
_, sslFIPSOutput := execContainer(t, cli, ctr.ID, "", []string{"bash", "-c", "echo 'DISPLAY QMGR SSLKEYR CERTLABL SSLFIPS' | runmqsc"})
|
||||
if !strings.Contains(sslFIPSOutput, "SSLKEYR( )") {
|
||||
t.Errorf("Expected SSLKEYR to be ' ' but it is not; got \"%v\"", sslFIPSOutput)
|
||||
}
|
||||
if !strings.Contains(sslFIPSOutput, "CERTLABL( )") {
|
||||
t.Errorf("Expected CERTLABL to be blank but it is not; got \"%v\"", sslFIPSOutput)
|
||||
}
|
||||
|
||||
if !strings.Contains(sslFIPSOutput, "SSLFIPS(NO)") {
|
||||
t.Errorf("Expected SSLFIPS to be 'NO' but it is not; got \"%v\"", sslFIPSOutput)
|
||||
}
|
||||
|
||||
// Stop the container cleanly
|
||||
stopContainer(t, cli, ctr.ID)
|
||||
}
|
||||
|
||||
// Verifies SSLFIPS is set to NO if MQ_ENABLE_FIPS=tru (invalid value)
|
||||
func TestSSLFIPSInvalidValue(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cli, err := client.NewClientWithOpts(client.FromEnv)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
containerConfig := container.Config{
|
||||
Env: []string{
|
||||
"LICENSE=accept",
|
||||
"MQ_QMGR_NAME=QM1",
|
||||
"MQ_ENABLE_EMBEDDED_WEB_SERVER=false",
|
||||
"MQ_ENABLE_FIPS=tru",
|
||||
},
|
||||
Image: imageName(),
|
||||
}
|
||||
hostConfig := container.HostConfig{
|
||||
Binds: []string{
|
||||
coverageBind(t),
|
||||
tlsDir(t, false) + ":/etc/mqm/pki/keys/default",
|
||||
},
|
||||
}
|
||||
networkingConfig := network.NetworkingConfig{}
|
||||
ctr, err := cli.ContainerCreate(context.Background(), &containerConfig, &hostConfig, &networkingConfig, t.Name())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer cleanContainer(t, cli, ctr.ID)
|
||||
startContainer(t, cli, ctr.ID)
|
||||
waitForReady(t, cli, ctr.ID)
|
||||
|
||||
// execute runmqsc to display qmgr SSLKEYR, SSLFIPS and CERTLABL attibutes.
|
||||
// Search the console output for exepcted values
|
||||
_, sslFIPSOutput := execContainer(t, cli, ctr.ID, "", []string{"bash", "-c", "echo 'DISPLAY QMGR SSLKEYR CERTLABL SSLFIPS' | runmqsc"})
|
||||
if !strings.Contains(sslFIPSOutput, "SSLKEYR(/run/runmqserver/tls/key)") {
|
||||
t.Errorf("Expected SSLKEYR to be '/run/runmqserver/tls/key' but it is not; got \"%v\"", sslFIPSOutput)
|
||||
}
|
||||
|
||||
if !strings.Contains(sslFIPSOutput, "CERTLABL(default)") {
|
||||
t.Errorf("Expected CERTLABL to be 'default' but it is not; got \"%v\"", sslFIPSOutput)
|
||||
}
|
||||
|
||||
if !strings.Contains(sslFIPSOutput, "SSLFIPS(NO)") {
|
||||
t.Errorf("Expected SSLFIPS to be 'NO' but it is not; got \"%v\"", sslFIPSOutput)
|
||||
}
|
||||
|
||||
// Stop the container cleanly
|
||||
stopContainer(t, cli, ctr.ID)
|
||||
}
|
||||
|
||||
// Container creation fails when invalid certs are passed and MQ_ENABLE_FIPS set true
|
||||
func TestSSLFIPSBadCerts(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cli, err := client.NewClientWithOpts(client.FromEnv)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
containerConfig := container.Config{
|
||||
Env: []string{
|
||||
"LICENSE=accept",
|
||||
"MQ_QMGR_NAME=QM1",
|
||||
"MQ_ENABLE_EMBEDDED_WEB_SERVER=false",
|
||||
"MQ_ENABLE_FIPS=true",
|
||||
},
|
||||
Image: imageName(),
|
||||
}
|
||||
hostConfig := container.HostConfig{
|
||||
Binds: []string{
|
||||
coverageBind(t),
|
||||
tlsDirInvalid(t, false) + ":/etc/mqm/pki/keys/default",
|
||||
},
|
||||
}
|
||||
networkingConfig := network.NetworkingConfig{}
|
||||
ctr, err := cli.ContainerCreate(context.Background(), &containerConfig, &hostConfig, &networkingConfig, t.Name())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer cleanContainer(t, cli, ctr.ID)
|
||||
startContainer(t, cli, ctr.ID)
|
||||
|
||||
rc := waitForContainer(t, cli, ctr.ID, 20*time.Second)
|
||||
// Expect return code 1 if container failed to create.
|
||||
if rc == 1 {
|
||||
// Get container logs and search for specific message.
|
||||
logs := inspectLogs(t, cli, ctr.ID)
|
||||
if strings.Contains(logs, "Failed to parse private key") {
|
||||
t.Logf("Container creating failed because of invalid certifates")
|
||||
}
|
||||
} else {
|
||||
// Some other error occurred.
|
||||
t.Errorf("Expected rc=0, got rc=%v", rc)
|
||||
}
|
||||
|
||||
// Stop the container cleanly
|
||||
|
||||
@@ -86,6 +86,10 @@ func tlsDirWithCA(t *testing.T, unixPath bool) string {
|
||||
return filepath.Join(getCwd(t, unixPath), "../tlscacert")
|
||||
}
|
||||
|
||||
func tlsDirInvalid(t *testing.T, unixPath bool) string {
|
||||
return filepath.Join(getCwd(t, unixPath), "../tlsinvalidcert")
|
||||
}
|
||||
|
||||
// runJMSTests runs a container with a JMS client, which connects to the queue manager container with the specified ID
|
||||
func runJMSTests(t *testing.T, cli *client.Client, ID string, tls bool, user, password string, ibmjre string, cipherName string) {
|
||||
containerConfig := container.Config{
|
||||
@@ -201,7 +205,7 @@ func createTLSConfig(t *testing.T, certFile, password string) *tls.Config {
|
||||
}
|
||||
}
|
||||
|
||||
func testRESTAdmin(t *testing.T, cli *client.Client, ID string, tlsConfig *tls.Config) {
|
||||
func testRESTAdmin(t *testing.T, cli *client.Client, ID string, tlsConfig *tls.Config, errorExpected string) {
|
||||
httpClient := http.Client{
|
||||
Timeout: time.Duration(30 * time.Second),
|
||||
Transport: &http.Transport{
|
||||
@@ -213,9 +217,15 @@ func testRESTAdmin(t *testing.T, cli *client.Client, ID string, tlsConfig *tls.C
|
||||
req.SetBasicAuth("admin", defaultAdminPassword)
|
||||
resp, err := httpClient.Do(req)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
if len(errorExpected) > 0 {
|
||||
if !strings.Contains(err.Error(), errorExpected) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
} else {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
if resp != nil && resp.StatusCode != http.StatusOK {
|
||||
t.Errorf("Expected HTTP status code %v from 'GET installation'; got %v", http.StatusOK, resp.StatusCode)
|
||||
}
|
||||
}
|
||||
@@ -238,7 +248,7 @@ func logHTTPResponse(t *testing.T, resp *http.Response) {
|
||||
t.Logf("HTTP response: %v", string(d))
|
||||
}
|
||||
|
||||
func testRESTMessaging(t *testing.T, cli *client.Client, ID string, tlsConfig *tls.Config, qmName string, user string, password string) {
|
||||
func testRESTMessaging(t *testing.T, cli *client.Client, ID string, tlsConfig *tls.Config, qmName string, user string, password string, errorExpected string) {
|
||||
httpClient := http.Client{
|
||||
Timeout: time.Duration(30 * time.Second),
|
||||
Transport: &http.Transport{
|
||||
@@ -255,10 +265,19 @@ func testRESTMessaging(t *testing.T, cli *client.Client, ID string, tlsConfig *t
|
||||
logHTTPRequest(t, req)
|
||||
resp, err := httpClient.Do(req)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
if len(errorExpected) > 0 {
|
||||
if strings.Contains(err.Error(), errorExpected) {
|
||||
t.Logf("Error contains expected '%s' value", errorExpected)
|
||||
return
|
||||
} else {
|
||||
t.Fatal(err)
|
||||
}
|
||||
} else {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
logHTTPResponse(t, resp)
|
||||
if resp.StatusCode != http.StatusCreated {
|
||||
if resp != nil && resp.StatusCode != http.StatusCreated {
|
||||
t.Errorf("Expected HTTP status code %v from 'POST to queue'; got %v", http.StatusOK, resp.StatusCode)
|
||||
t.Logf("HTTP response: %+v", resp)
|
||||
t.Fail()
|
||||
@@ -286,3 +305,28 @@ func testRESTMessaging(t *testing.T, cli *client.Client, ID string, tlsConfig *t
|
||||
t.Errorf("Expected payload to be \"%s\"; got \"%s\"", putMessage, gotMessage)
|
||||
}
|
||||
}
|
||||
|
||||
// createTLSConfig creates a tls.Config which trusts the specified certificate
|
||||
func createTLSConfigWithCipher(t *testing.T, certFile, password string, ciphers []uint16) *tls.Config {
|
||||
// Get the SystemCertPool, continue with an empty pool on error
|
||||
certs, err := x509.SystemCertPool()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// Read in the cert file
|
||||
cert, err := ioutil.ReadFile(certFile)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// Append our cert to the system pool
|
||||
ok := certs.AppendCertsFromPEM(cert)
|
||||
if !ok {
|
||||
t.Fatal("No certs appended")
|
||||
}
|
||||
// Trust the augmented cert pool in our client
|
||||
return &tls.Config{
|
||||
InsecureSkipVerify: false,
|
||||
RootCAs: certs,
|
||||
CipherSuites: ciphers,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2017, 2022
|
||||
© Copyright IBM Corporation 2017, 2023
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -52,8 +52,8 @@ func TestLicenseNotSet(t *testing.T) {
|
||||
expectTerminationMessage(t, cli, id)
|
||||
}
|
||||
|
||||
//Start container with LICENSE environment variable set to view.
|
||||
//Check that container starts and display license text
|
||||
// Start container with LICENSE environment variable set to view.
|
||||
// Check that container starts and display license text
|
||||
func TestLicenseView(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@@ -77,45 +77,20 @@ func TestLicenseView(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
//Start a container with qm grace set to x seconds
|
||||
//Check that when the container is stopped that the command endmqm has option -tp and x
|
||||
func TestEndMQMOpts(t *testing.T) {
|
||||
t.Parallel()
|
||||
cli, err := client.NewClientWithOpts(client.FromEnv)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
containerConfig := container.Config{
|
||||
Env: []string{"LICENSE=accept", "MQ_GRACE_PERIOD=27"},
|
||||
}
|
||||
|
||||
id := runContainer(t, cli, &containerConfig)
|
||||
defer cleanContainer(t, cli, id)
|
||||
waitForReady(t, cli, id)
|
||||
killContainer(t, cli, id, "SIGTERM")
|
||||
_, out := execContainer(t, cli, id, "", []string{"bash", "-c", "ps -ef | grep 'endmqm -w -r -tp 27'"})
|
||||
t.Log(out)
|
||||
if !strings.Contains(out, "endmqm -w -r -tp 27") {
|
||||
t.Errorf("Expected endmqm options endmqm -w -r -tp 27; got \"%v\"", out)
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// Actual test function for TestGoldenPathNoMetrics & TestGoldenPathWithMetrics
|
||||
func goldenPath(t *testing.T, metric bool) {
|
||||
func goldenPath(t *testing.T, metrics bool) {
|
||||
cli, err := client.NewClientWithOpts(client.FromEnv)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -123,18 +98,34 @@ func goldenPath(t *testing.T, metric bool) {
|
||||
containerConfig := container.Config{
|
||||
Env: []string{"LICENSE=accept", "MQ_QMGR_NAME=qm1"},
|
||||
}
|
||||
if metric {
|
||||
if metrics {
|
||||
containerConfig.Env = append(containerConfig.Env, "MQ_ENABLE_METRICS=true")
|
||||
}
|
||||
|
||||
id := runContainer(t, cli, &containerConfig)
|
||||
defer cleanContainer(t, cli, id)
|
||||
waitForReady(t, cli, id)
|
||||
|
||||
//By default AMQ5041I,AMQ5052I,AMQ5051I,AMQ5037I,AMQ5975I are excluded
|
||||
jsonLogs := inspectLogs(t, cli, id)
|
||||
|
||||
isMessageFound := scanForExcludedEntries(jsonLogs)
|
||||
|
||||
if isMessageFound == true {
|
||||
t.Errorf("Expected to exclude messageId by default; but messageId \"%v\" is present", jsonLogs)
|
||||
}
|
||||
|
||||
if strings.Contains(jsonLogs, "CWWKF0011I") {
|
||||
t.Errorf("Web Server is off....CWWKF0011I message id is not expected")
|
||||
}
|
||||
|
||||
t.Run("Validate Default LogFilePages", func(t *testing.T) {
|
||||
testLogFilePages(t, cli, id, "qm1", "4096")
|
||||
})
|
||||
// Stop the container cleanly
|
||||
stopContainer(t, cli, id)
|
||||
}
|
||||
|
||||
|
||||
func utilTestNoQueueManagerName(t *testing.T, hostName string, expectedName string) {
|
||||
search := "QMNAME(" + expectedName + ")"
|
||||
cli, err := client.NewClientWithOpts(client.FromEnv)
|
||||
@@ -1180,28 +1171,6 @@ func jsonLogFormat(t *testing.T, metric bool) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestBadLogFormat(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cli, err := client.NewClientWithOpts(client.FromEnv)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
containerConfig := container.Config{
|
||||
Env: []string{
|
||||
"LICENSE=accept",
|
||||
"LOG_FORMAT=fake",
|
||||
},
|
||||
}
|
||||
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)
|
||||
}
|
||||
expectTerminationMessage(t, cli, id)
|
||||
}
|
||||
|
||||
// TestMQJSONDisabled tests the case where MQ's JSON logging feature is
|
||||
// specifically disabled (which will disable log mirroring)
|
||||
func TestMQJSONDisabled(t *testing.T) {
|
||||
@@ -1483,3 +1452,450 @@ func TestStartedCheckWithNoNewPrivileges(t *testing.T) {
|
||||
func TestStartedCheckWithNewPrivileges(t *testing.T) {
|
||||
utilTestStartedCheck(t, false)
|
||||
}
|
||||
|
||||
// Start a container with qm grace set to x seconds
|
||||
// Check that when the container is stopped that the command endmqm has option -tp and x
|
||||
func TestEndMQMOpts(t *testing.T) {
|
||||
t.Parallel()
|
||||
cli, err := client.NewClientWithOpts(client.FromEnv)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
containerConfig := container.Config{
|
||||
Env: []string{"LICENSE=accept", "MQ_GRACE_PERIOD=27"},
|
||||
}
|
||||
|
||||
id := runContainer(t, cli, &containerConfig)
|
||||
defer cleanContainer(t, cli, id)
|
||||
waitForReady(t, cli, id)
|
||||
killContainer(t, cli, id, "SIGTERM")
|
||||
_, out := execContainer(t, cli, id, "", []string{"bash", "-c", "ps -ef | grep 'endmqm -w -r -tp 27'"})
|
||||
t.Log(out)
|
||||
if !strings.Contains(out, "endmqm -w -r -tp 27") {
|
||||
t.Errorf("Expected endmqm options endmqm -w -r -tp 27; got \"%v\"", out)
|
||||
}
|
||||
}
|
||||
|
||||
// TestCustomLogFilePages starts a qmgr with a custom number of logfilepages set.
|
||||
// Check that the number of logfilepages matches.
|
||||
func TestCustomLogFilePages(t *testing.T) {
|
||||
t.Parallel()
|
||||
cli, err := client.NewClientWithOpts(client.FromEnv)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
containerConfig := container.Config{
|
||||
Env: []string{"LICENSE=accept", "MQ_QMGR_LOG_FILE_PAGES=8192", "MQ_QMGR_NAME=qmlfp"},
|
||||
}
|
||||
|
||||
id := runContainer(t, cli, &containerConfig)
|
||||
defer cleanContainer(t, cli, id)
|
||||
waitForReady(t, cli, id)
|
||||
|
||||
testLogFilePages(t, cli, id, "qmlfp", "8192")
|
||||
}
|
||||
|
||||
// TestLoggingConsoleSource tests default behavior which is
|
||||
// MQ_LOGGING_CONSOLE_SOURCE set to qmgr,web
|
||||
func TestLoggingConsoleSource(t *testing.T) {
|
||||
|
||||
t.Parallel()
|
||||
cli, err := client.NewClientWithOpts(client.FromEnv)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
containerConfig := container.Config{
|
||||
Env: []string{
|
||||
"LICENSE=accept",
|
||||
"MQ_QMGR_NAME=qm1",
|
||||
"MQ_ENABLE_EMBEDDED_WEB_SERVER=true",
|
||||
},
|
||||
}
|
||||
id := runContainer(t, cli, &containerConfig)
|
||||
defer cleanContainer(t, cli, id)
|
||||
waitForReady(t, cli, id)
|
||||
|
||||
jsonLogs, errJson := waitForMessageInLog(t, cli, id, "AMQ6206I")
|
||||
if errJson != nil {
|
||||
t.Errorf("%v", errJson)
|
||||
}
|
||||
|
||||
isMessageFound := scanForExcludedEntries(jsonLogs)
|
||||
|
||||
if isMessageFound == true {
|
||||
t.Errorf("Expected to exclude messageId by default; but messageId \"%v\" is present", jsonLogs)
|
||||
}
|
||||
|
||||
// Stop the container cleanly
|
||||
stopContainer(t, cli, id)
|
||||
}
|
||||
|
||||
// TestOldBehaviorWebConsole sets LOG_FORMAT to json and verify logs are indeed in json format
|
||||
func TestOldBehaviorWebConsole(t *testing.T) {
|
||||
|
||||
t.Parallel()
|
||||
cli, err := client.NewClientWithOpts(client.FromEnv)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
containerConfig := container.Config{
|
||||
Env: []string{
|
||||
"LICENSE=accept",
|
||||
"MQ_QMGR_NAME=qm1",
|
||||
"LOG_FORMAT=json",
|
||||
},
|
||||
}
|
||||
id := runContainer(t, cli, &containerConfig)
|
||||
defer cleanContainer(t, cli, id)
|
||||
waitForReady(t, cli, id)
|
||||
jsonLogs := inspectLogs(t, cli, id)
|
||||
|
||||
isMessageFound := scanForExcludedEntries(jsonLogs)
|
||||
|
||||
if isMessageFound == true {
|
||||
t.Errorf("Expected to exclude messageId by default; but messageId \"%v\" is present", jsonLogs)
|
||||
}
|
||||
|
||||
if strings.Contains(jsonLogs, "Environment variable LOG_FORMAT is deprecated. Use MQ_LOGGING_CONSOLE_FORMAT instead.") {
|
||||
t.Logf("Expected Message stating LOG_FORMAT is deprecated is present in the log")
|
||||
} else {
|
||||
t.Errorf("Expected Message stating LOG_FORMAT is deprecated is not in the log")
|
||||
}
|
||||
|
||||
isValidJSON := checkLogForValidJSON(jsonLogs)
|
||||
|
||||
if !isValidJSON {
|
||||
t.Fatalf("Expected all log lines to be valid JSON. But got error %v ", err)
|
||||
}
|
||||
|
||||
// Stop the container cleanly
|
||||
stopContainer(t, cli, id)
|
||||
}
|
||||
|
||||
// TestLoggingConsoleWithContRestart restarts the container and checks
|
||||
// that setting of env variable persists
|
||||
func TestLoggingConsoleWithContRestart(t *testing.T) {
|
||||
|
||||
t.Parallel()
|
||||
|
||||
cli, err := client.NewClientWithOpts(client.FromEnv)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
containerConfig := container.Config{
|
||||
Env: []string{
|
||||
"LICENSE=accept",
|
||||
"MQ_QMGR_NAME=qm1",
|
||||
"MQ_LOGGING_CONSOLE_SOURCE=qmgr",
|
||||
},
|
||||
}
|
||||
id := runContainer(t, cli, &containerConfig)
|
||||
|
||||
defer cleanContainer(t, cli, id)
|
||||
waitForReady(t, cli, id)
|
||||
|
||||
jsonLogs, errJson := waitForMessageInLog(t, cli, id, "AMQ6206I")
|
||||
if errJson != nil {
|
||||
t.Errorf("%v", errJson)
|
||||
}
|
||||
|
||||
isMessageFound := scanForExcludedEntries(jsonLogs)
|
||||
|
||||
if isMessageFound == true {
|
||||
t.Errorf("Expected to exclude messageId by default; but messageId \"%v\" is present", jsonLogs)
|
||||
}
|
||||
|
||||
stopContainer(t, cli, id)
|
||||
startContainer(t, cli, id)
|
||||
waitForReady(t, cli, id)
|
||||
|
||||
jsonLogs = inspectLogs(t, cli, id)
|
||||
|
||||
if !strings.Contains(jsonLogs, "Stopped queue manager") || strings.Contains(jsonLogs, "CWWKF0011I") {
|
||||
t.Errorf("CWWKF0011I which is not expected is present!!!!!")
|
||||
}
|
||||
|
||||
isMessageFoundAfterRestart := scanForExcludedEntries(jsonLogs)
|
||||
|
||||
if isMessageFoundAfterRestart == true {
|
||||
t.Errorf("Expected to exclude messageId by default; but messageId \"%v\" is present", jsonLogs)
|
||||
}
|
||||
|
||||
// Stop the container cleanly
|
||||
stopContainer(t, cli, id)
|
||||
}
|
||||
|
||||
// TestLoggingWithQmgrAndExcludeId tests MQ_LOGGING_CONSOLE_SOURCE set to qmgr
|
||||
// and exclude ID set to amq7230I.
|
||||
|
||||
func TestLoggingWithQmgrAndExcludeId(t *testing.T) {
|
||||
qmgrName := "qm1"
|
||||
|
||||
t.Parallel()
|
||||
|
||||
cli, err := client.NewClientWithOpts(client.FromEnv)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
containerConfig := container.Config{
|
||||
Env: []string{
|
||||
"LICENSE=accept",
|
||||
"MQ_QMGR_NAME=qm1",
|
||||
"MQ_LOGGING_CONSOLE_SOURCE=qmgr",
|
||||
"MQ_LOGGING_CONSOLE_FORMAT=json",
|
||||
"MQ_LOGGING_CONSOLE_EXCLUDE_ID=amq7230I",
|
||||
},
|
||||
}
|
||||
|
||||
dir := "/var/mqm/qmgrs/" + qmgrName + "/errors"
|
||||
|
||||
id := runContainer(t, cli, &containerConfig)
|
||||
defer cleanContainer(t, cli, id)
|
||||
waitForReady(t, cli, id)
|
||||
|
||||
jsonLogs, errJson := waitForMessageInLog(t, cli, id, "AMQ6206I")
|
||||
if errJson != nil {
|
||||
t.Errorf("%v", errJson)
|
||||
}
|
||||
|
||||
isValidJSON := checkLogForValidJSON(jsonLogs)
|
||||
|
||||
if !isValidJSON {
|
||||
t.Fatalf("Expected all log lines to be valid JSON. But got error %v ", err)
|
||||
}
|
||||
|
||||
if strings.Contains(jsonLogs, "AMQ7230I") || strings.Contains(jsonLogs, "CWWKF0011I") {
|
||||
t.Errorf("Expected to exclude messageId by default; but messageId \"%v\" is present", jsonLogs)
|
||||
}
|
||||
|
||||
stopContainer(t, cli, id)
|
||||
|
||||
//checking that message is only excluded from the console log, but not from the MQ error log
|
||||
b := copyFromContainer(t, cli, id, filepath.Join(dir, "AMQERR01.json"))
|
||||
|
||||
foundInLog := 0
|
||||
r := bytes.NewReader(b)
|
||||
scannernew := bufio.NewScanner(r)
|
||||
for scannernew.Scan() {
|
||||
textData := scannernew.Text()
|
||||
|
||||
if strings.Contains(textData, "AMQ7230I") {
|
||||
foundInLog = 1
|
||||
}
|
||||
}
|
||||
if foundInLog == 0 {
|
||||
t.Errorf("mesageID AMQ7230I is not present in MQ LOG!!!!")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// TestLoggingConsoleSetToWeb tests MQ_LOGGING_CONSOLE_SOURCE set to web
|
||||
func TestLoggingConsoleSetToWeb(t *testing.T) {
|
||||
|
||||
t.Parallel()
|
||||
cli, err := client.NewClientWithOpts(client.FromEnv)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
containerConfig := container.Config{
|
||||
Env: []string{
|
||||
"LICENSE=accept",
|
||||
"MQ_QMGR_NAME=qm1",
|
||||
"MQ_ENABLE_EMBEDDED_WEB_SERVER=true",
|
||||
"MQ_LOGGING_CONSOLE_SOURCE=web",
|
||||
"MQ_LOGGING_CONSOLE_EXCLUDE_ID=CWWKG0028A,CWWKS4105I",
|
||||
"MQ_LOGGING_CONSOLE_FORMAT=json",
|
||||
},
|
||||
}
|
||||
id := runContainer(t, cli, &containerConfig)
|
||||
defer cleanContainer(t, cli, id)
|
||||
waitForReady(t, cli, id)
|
||||
|
||||
jsonLogs, errJson := waitForMessageInLog(t, cli, id, "CWWKF0011I")
|
||||
if errJson != nil {
|
||||
t.Errorf("%v", errJson)
|
||||
}
|
||||
|
||||
if strings.Contains(jsonLogs, "AMQ6206I") {
|
||||
t.Errorf("Logging source is set to web, Qmgr message \"%v\" should be excluded!!!", jsonLogs)
|
||||
}
|
||||
|
||||
if strings.Contains(jsonLogs, "AMQ5041I") || strings.Contains(jsonLogs, "AMQ5052I") ||
|
||||
strings.Contains(jsonLogs, "AMQ5051I") || strings.Contains(jsonLogs, "AMQ5037I") ||
|
||||
strings.Contains(jsonLogs, "AMQ5975I") || strings.Contains(jsonLogs, "CWWKG0028A") ||
|
||||
strings.Contains(jsonLogs, "CWWKS4105I") {
|
||||
t.Errorf("Expected to exclude messageId by default; but messageId \"%v\" is present", jsonLogs)
|
||||
}
|
||||
|
||||
// Stop the container cleanly
|
||||
stopContainer(t, cli, id)
|
||||
}
|
||||
|
||||
// TestLoggingConsoleSetToQmgr test sets LOG_FORMAT to BASIC and MQ_LOGGING_CONSOLE_FORMAT to
|
||||
// json and check that log is in json format
|
||||
func TestLoggingConsoleSetToQmgr(t *testing.T) {
|
||||
t.Parallel()
|
||||
cli, err := client.NewClientWithOpts(client.FromEnv)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
containerConfig := container.Config{
|
||||
Env: []string{
|
||||
"LICENSE=accept",
|
||||
"MQ_QMGR_NAME=qm1",
|
||||
"MQ_ENABLE_EMBEDDED_WEB_SERVER=false",
|
||||
"MQ_LOGGING_CONSOLE_SOURCE=qmgr",
|
||||
"LOG_FORMAT=BASIC",
|
||||
"MQ_LOGGING_CONSOLE_FORMAT=json",
|
||||
},
|
||||
}
|
||||
id := runContainer(t, cli, &containerConfig)
|
||||
defer cleanContainer(t, cli, id)
|
||||
waitForReady(t, cli, id)
|
||||
|
||||
jsonLogs, errJson := waitForMessageInLog(t, cli, id, "AMQ6206I")
|
||||
if errJson != nil {
|
||||
t.Errorf("%v", errJson)
|
||||
}
|
||||
|
||||
isMessageFound := scanForExcludedEntries(jsonLogs)
|
||||
|
||||
if isMessageFound == true {
|
||||
t.Errorf("Expected to exclude messageId by default; but messageId \"%v\" is present", jsonLogs)
|
||||
}
|
||||
|
||||
isValidJSON := checkLogForValidJSON(jsonLogs)
|
||||
|
||||
if !isValidJSON {
|
||||
t.Fatalf("Expected all log lines to be valid JSON. But got error %v ", err)
|
||||
}
|
||||
|
||||
// Stop the container cleanly
|
||||
stopContainer(t, cli, id)
|
||||
|
||||
}
|
||||
|
||||
// Test queue manager with both personal and CA certificate having the same DN
|
||||
func TestSameSubDNError(t *testing.T) {
|
||||
expectedOutput := "Error: The Subject DN of the Issuer Certificate and the Queue Manager are same"
|
||||
utilSubDNTest(t, "../tlssamesubdn", "true", expectedOutput, false)
|
||||
}
|
||||
|
||||
// Test queue manager with both personal and CA certificate having the same DN
|
||||
// but override the changed behavior via environment variable
|
||||
func TestSameSubDNErrorOverride(t *testing.T) {
|
||||
expectedOutput := "Failed to relabel certificate for"
|
||||
utilSubDNTest(t, "../tlssamesubdn", "false", expectedOutput, false)
|
||||
}
|
||||
|
||||
// Test queue manager with root CA certificate
|
||||
func TestWithCASignedCerts(t *testing.T) {
|
||||
expectedOutput := "Creating queue manager MQQM"
|
||||
utilSubDNTest(t, "../tlsdifferentsubdn", "true", expectedOutput, true)
|
||||
}
|
||||
|
||||
// Test queue manager with intermediate CA certificate
|
||||
func TestWithIntermediateCASignedCerts(t *testing.T) {
|
||||
expectedOutput := "Creating queue manager MQQM"
|
||||
utilSubDNTest(t, "../tlsintermediateca", "true", expectedOutput, true)
|
||||
}
|
||||
|
||||
// Scan the console output for required content.
|
||||
func scanForText(output string, prefix string, findText string) (int, bool) {
|
||||
var count int
|
||||
var found bool
|
||||
scanner := bufio.NewScanner(strings.NewReader(output))
|
||||
for scanner.Scan() {
|
||||
s := scanner.Text()
|
||||
if strings.HasPrefix(s, prefix) {
|
||||
count++
|
||||
}
|
||||
if strings.Contains(s, findText) {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
return count, found
|
||||
}
|
||||
|
||||
// Utility function to test Certificate relabel issues.
|
||||
func utilSubDNTest(t *testing.T, certPath string, overrideFlag string, expecteOutPut string, waitLong bool) {
|
||||
t.Parallel()
|
||||
|
||||
cli, err := client.NewClientWithOpts(client.FromEnv)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
containerConfig := container.Config{
|
||||
Env: []string{
|
||||
"LICENSE=accept",
|
||||
"MQ_QMGR_NAME=QM1",
|
||||
"MQ_ENABLE_CERT_VALIDATION=" + overrideFlag,
|
||||
},
|
||||
Image: imageName(),
|
||||
}
|
||||
hostConfig := container.HostConfig{
|
||||
Binds: []string{
|
||||
coverageBind(t),
|
||||
tlsDirDN(t, false, certPath) + ":/etc/mqm/pki/keys/QM1",
|
||||
},
|
||||
}
|
||||
|
||||
networkingConfig := network.NetworkingConfig{}
|
||||
ctr, err := cli.ContainerCreate(context.Background(), &containerConfig, &hostConfig, &networkingConfig, t.Name())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer cleanContainer(t, cli, ctr.ID)
|
||||
startContainer(t, cli, ctr.ID)
|
||||
|
||||
if waitLong {
|
||||
waitForReady(t, cli, ctr.ID)
|
||||
_, output := execContainer(t, cli, ctr.ID, "", []string{"bash", "-c", "echo 'DISPLAY QMGR SSLKEYR CERTLABL SSLFIPS' | runmqsc"})
|
||||
if !strings.Contains(output, "SSLKEYR(/run/runmqserver/tls/key)") {
|
||||
t.Errorf("Expected SSLKEYR to be '/run/runmqserver/tls/key' but it is not; got \"%v\"", output)
|
||||
}
|
||||
|
||||
if !strings.Contains(output, "CERTLABL(QM1)") {
|
||||
t.Errorf("Expected CERTLABL to be 'default' but it is not; got \"%v\"", output)
|
||||
}
|
||||
_, output = execContainer(t, cli, ctr.ID, "", []string{"bash", "-c", "runmqakm -cert -list -type cms -db /run/runmqserver/tls/key.kdb -stashed"})
|
||||
if strings.EqualFold(t.Name(), "TestWithCASignedCerts") {
|
||||
// There should be one personal certificate and one trusted certificate.
|
||||
count, found := scanForText(output, "!", "CN=MQMFTQM,OU=ISL,O=IBM,L=BLR,ST=KA,C=IN")
|
||||
if count != 1 && !found {
|
||||
t.Errorf("Expected 1 trusted certificate with name containing CN=MQMFTQM. But found %v", output)
|
||||
}
|
||||
// One personal certificate that relabeld as QM1
|
||||
count, found = scanForText(output, "-", "QM1")
|
||||
if count != 1 && !found {
|
||||
t.Errorf("Expected 1 personal certificate with name containing QM1. But found %v", output)
|
||||
}
|
||||
} else if strings.EqualFold(t.Name(), "TestWithIntermediateCASignedCerts") {
|
||||
// There should be one personal certificate and two trusted certificates
|
||||
// an intermediate CA and the root CA.
|
||||
count, found := scanForText(output, "!", "ST=HANTS,C=GB")
|
||||
if count != 2 && !found {
|
||||
t.Errorf("Expected 2 trusted certificate with name containing 'ST=HANTS,C=GB'. But found %v", output)
|
||||
}
|
||||
// One personal certificate that is correctly relabeld as QM1
|
||||
count, found = scanForText(output, "-", "QM1")
|
||||
if count != 1 && !found {
|
||||
t.Errorf("Expected 1 personal certificate with name containing QM1. But found %v", output)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
rc := waitForContainer(t, cli, ctr.ID, 20*time.Second)
|
||||
// Expect return code 1 if container failed to create.
|
||||
if rc == 1 {
|
||||
// Get container logs and search for specific message.
|
||||
logs := inspectLogs(t, cli, ctr.ID)
|
||||
if !strings.Contains(logs, expecteOutPut) {
|
||||
t.Errorf("Container creating failed because of invalid certifates")
|
||||
}
|
||||
} else {
|
||||
// Some other error occurred
|
||||
t.Errorf("Some other error occurred %v", rc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2017, 2022
|
||||
© Copyright IBM Corporation 2017, 2023
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -745,6 +745,7 @@ func inspectLogs(t *testing.T, cli *client.Client, ID string) string {
|
||||
t.Fatal(err)
|
||||
}
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
// Each output line has a header, which needs to be removed
|
||||
_, err = stdcopy.StdCopy(buf, buf, reader)
|
||||
if err != nil {
|
||||
@@ -889,3 +890,99 @@ func getMQVersion(t *testing.T, cli *client.Client) (string, error) {
|
||||
version := inspect.ContainerConfig.Labels["version"]
|
||||
return version, nil
|
||||
}
|
||||
|
||||
// scanForExcludedEntries scans for default excluded messages
|
||||
func scanForExcludedEntries(msg string) bool {
|
||||
if strings.Contains(msg, "AMQ5041I") || strings.Contains(msg, "AMQ5052I") ||
|
||||
strings.Contains(msg, "AMQ5051I") || strings.Contains(msg, "AMQ5037I") ||
|
||||
strings.Contains(msg, "AMQ5975I") {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// checkLogForValidJSON checks if the message is in Json format
|
||||
func checkLogForValidJSON(jsonLogs string) bool {
|
||||
scanner := bufio.NewScanner(strings.NewReader(jsonLogs))
|
||||
for scanner.Scan() {
|
||||
var obj map[string]interface{}
|
||||
s := scanner.Text()
|
||||
err := json.Unmarshal([]byte(s), &obj)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// runContainerWithAllConfig creates and starts a container, using the supplied ContainerConfig, HostConfig,
|
||||
// NetworkingConfig, and container name (or the value of t.Name if containerName="").
|
||||
func runContainerWithAllConfigError(t *testing.T, cli *client.Client, containerConfig *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, containerName string) (string, error) {
|
||||
if containerName == "" {
|
||||
containerName = t.Name()
|
||||
}
|
||||
if containerConfig.Image == "" {
|
||||
containerConfig.Image = imageName()
|
||||
}
|
||||
// Always run as a random user, unless the test has specified otherwise
|
||||
if containerConfig.User == "" {
|
||||
containerConfig.User = generateRandomUID()
|
||||
}
|
||||
// if coverage
|
||||
containerConfig.Env = append(containerConfig.Env, "COVERAGE_FILE="+t.Name()+".cov")
|
||||
containerConfig.Env = append(containerConfig.Env, "EXIT_CODE_FILE="+getExitCodeFilename(t))
|
||||
t.Logf("Running container (%s)", containerConfig.Image)
|
||||
ctr, err := cli.ContainerCreate(context.Background(), containerConfig, hostConfig, networkingConfig, containerName)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
err = startContainerError(t, cli, ctr.ID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return ctr.ID, nil
|
||||
}
|
||||
|
||||
func startContainerError(t *testing.T, cli *client.Client, ID string) error {
|
||||
t.Logf("Starting container: %v", ID)
|
||||
startOptions := types.ContainerStartOptions{}
|
||||
err := cli.ContainerStart(context.Background(), ID, startOptions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// testLogFilePages validates that the specified number of logFilePages is present in the qm.ini file.
|
||||
func testLogFilePages(t *testing.T, cli *client.Client, id string, qmName string, expectedLogFilePages string) {
|
||||
catIniFileCommand := fmt.Sprintf("cat /var/mqm/qmgrs/" + qmName + "/qm.ini")
|
||||
_, iniContent := execContainer(t, cli, id, "", []string{"bash", "-c", catIniFileCommand})
|
||||
|
||||
if !strings.Contains(iniContent, "LogFilePages="+expectedLogFilePages) {
|
||||
t.Errorf("Expected qm.ini to contain LogFilePages="+expectedLogFilePages+"; got qm.ini \"%v\"", iniContent)
|
||||
}
|
||||
}
|
||||
|
||||
//waitForMessageInLog will check for a particular message with wait
|
||||
func waitForMessageInLog(t *testing.T, cli *client.Client, id string, expecteMessageId string) (string, error) {
|
||||
var jsonLogs string
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
|
||||
defer cancel()
|
||||
for {
|
||||
select {
|
||||
case <-time.After(1 * time.Second):
|
||||
jsonLogs = inspectLogs(t, cli, id)
|
||||
if strings.Contains(jsonLogs, expecteMessageId) {
|
||||
return jsonLogs, nil
|
||||
}
|
||||
case <-ctx.Done():
|
||||
return "", fmt.Errorf("Expected message Id %s was not logged.", expecteMessageId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Returns fully qualified path
|
||||
func tlsDirDN(t *testing.T, unixPath bool, certPath string) string {
|
||||
return filepath.Join(getCwd(t, unixPath), certPath)
|
||||
}
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
module github.com/ibm-messaging/mq-container/test/docker
|
||||
|
||||
go 1.16
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/containerd/containerd v1.6.6 // indirect
|
||||
github.com/docker/distribution v2.8.1+incompatible // indirect
|
||||
// Note: This is not actually Docker v17.12!
|
||||
// Go modules require the use of semver, but Docker does not use semver and has not
|
||||
// [opted-in to use Go modules](https://github.com/golang/go/wiki/Modules#can-a-module-consume-a-package-that-has-not-opted-in-to-modules)
|
||||
@@ -17,6 +15,26 @@ require (
|
||||
// version 1.41 on the server, which is currently too new for the version of Docker in Travis (Ubuntu Bionic)
|
||||
github.com/docker/docker v17.12.0-ce-rc1.0.20210128214336-420b1d36250f+incompatible
|
||||
github.com/docker/go-connections v0.4.0
|
||||
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150 // indirect
|
||||
google.golang.org/grpc v1.46.0 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
|
||||
github.com/Microsoft/go-winio v0.5.2 // indirect
|
||||
github.com/containerd/containerd v1.6.18 // indirect
|
||||
github.com/docker/distribution v2.8.1+incompatible // indirect
|
||||
github.com/docker/go-units v0.4.0 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/gorilla/mux v1.8.0 // indirect
|
||||
github.com/morikuni/aec v1.0.0 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/sirupsen/logrus v1.8.1 // indirect
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect
|
||||
google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21 // indirect
|
||||
google.golang.org/grpc v1.47.0 // indirect
|
||||
google.golang.org/protobuf v1.28.0 // indirect
|
||||
gotest.tools v2.2.0+incompatible // indirect
|
||||
)
|
||||
|
||||
1264
test/docker/go.sum
1264
test/docker/go.sum
File diff suppressed because it is too large
Load Diff
@@ -16,9 +16,9 @@ limitations under the License.
|
||||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/client"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestNativeHABasic creates 3 containers in a Native HA queue manager configuration
|
||||
@@ -217,3 +217,96 @@ func TestNativeHASecureCipherSpec(t *testing.T) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// TestNativeHASecure creates 3 containers in a Native HA queue manager configuration
|
||||
// with HA TLS FIPS enabled, overrides the default CipherSpec, and ensures the queue manger
|
||||
// and replicas start as expected. This test uses FIPS compliant cipher.
|
||||
func TestNativeHASecureCipherSpecFIPS(t *testing.T) {
|
||||
cli, err := client.NewClientWithOpts(client.FromEnv)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
version, err := getMQVersion(t, cli)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if version < "9.2.2.0" {
|
||||
t.Skipf("Skipping %s as test requires at least MQ 9.2.2.0, but image is version %s", t.Name(), version)
|
||||
}
|
||||
|
||||
containerNames := [3]string{"QM1_1", "QM1_2", "QM1_3"}
|
||||
qmReplicaIDs := [3]string{}
|
||||
qmNetwork, err := createBridgeNetwork(cli, t)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer removeBridgeNetwork(cli, qmNetwork.ID)
|
||||
|
||||
for i := 0; i <= 2; i++ {
|
||||
containerConfig := getNativeHAContainerConfig(containerNames[i], containerNames, defaultHAPort)
|
||||
// MQ_NATIVE_HA_CIPHERSPEC is set a FIPS compliant cipherspec.
|
||||
containerConfig.Env = append(containerConfig.Env, "MQ_NATIVE_HA_TLS=true", "MQ_NATIVE_HA_CIPHERSPEC=TLS_RSA_WITH_AES_128_GCM_SHA256", "MQ_ENABLE_FIPS=true")
|
||||
hostConfig := getNativeHASecureHostConfig(t)
|
||||
networkingConfig := getNativeHANetworkConfig(qmNetwork.ID)
|
||||
|
||||
ctr := runContainerWithAllConfig(t, cli, &containerConfig, &hostConfig, &networkingConfig, containerNames[i])
|
||||
defer cleanContainer(t, cli, ctr)
|
||||
qmReplicaIDs[i] = ctr
|
||||
}
|
||||
|
||||
waitForReadyHA(t, cli, qmReplicaIDs)
|
||||
// Display the contents of qm.ini
|
||||
_, qmini := execContainer(t, cli, qmReplicaIDs[0], "", []string{"cat", "/var/mqm/qmgrs/QM1/qm.ini"})
|
||||
if !strings.Contains(qmini, "SSLFipsRequired=Yes") {
|
||||
t.Errorf("Expected SSLFipsRequired=Yes but it is not; got \"%v\"", qmini)
|
||||
}
|
||||
|
||||
_, err = getActiveReplicaInstances(t, cli, qmReplicaIDs)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestNativeHASecure creates 3 containers in a Native HA queue manager configuration
|
||||
// with HA TLS FIPS enabled with non-FIPS cipher, overrides the default CipherSpec, and
|
||||
// ensures the queue manger and replicas don't start as expected
|
||||
func TestNativeHASecureCipherSpecNonFIPSCipher(t *testing.T) {
|
||||
cli, err := client.NewClientWithOpts(client.FromEnv)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
version, err := getMQVersion(t, cli)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if version < "9.2.2.0" {
|
||||
t.Skipf("Skipping %s as test requires at least MQ 9.2.2.0, but image is version %s", t.Name(), version)
|
||||
}
|
||||
|
||||
containerNames := [3]string{"QM1_1", "QM1_2", "QM1_3"}
|
||||
qmReplicaIDs := [3]string{}
|
||||
qmNetwork, err := createBridgeNetwork(cli, t)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer removeBridgeNetwork(cli, qmNetwork.ID)
|
||||
|
||||
for i := 0; i <= 2; i++ {
|
||||
containerConfig := getNativeHAContainerConfig(containerNames[i], containerNames, defaultHAPort)
|
||||
// MQ_NATIVE_HA_CIPHERSPEC is set a FIPS non-compliant cipherspec - SSL_ECDHE_ECDSA_WITH_RC4_128_SHA
|
||||
containerConfig.Env = append(containerConfig.Env, "MQ_NATIVE_HA_TLS=true", "MQ_NATIVE_HA_CIPHERSPEC=TLS_RSA_WITH_AES_128_GCM_SHA256", "MQ_ENABLE_FIPS=true")
|
||||
hostConfig := getNativeHASecureHostConfig(t)
|
||||
networkingConfig := getNativeHANetworkConfig(qmNetwork.ID)
|
||||
|
||||
ctr, err := runContainerWithAllConfigError(t, cli, &containerConfig, &hostConfig, &networkingConfig, containerNames[i])
|
||||
defer cleanContainer(t, cli, ctr)
|
||||
// We expect container to fail in this case because the cipher is non-FIPS and we have asked for FIPS compliance
|
||||
// by setting MQ_ENABLE_FIPS=true
|
||||
if err == nil {
|
||||
t.Logf("Container start expected to fail but did not. %v", err)
|
||||
}
|
||||
qmReplicaIDs[i] = ctr
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ func getActiveReplicaInstances(t *testing.T, cli *client.Client, qmReplicaIDs [3
|
||||
|
||||
func waitForReadyHA(t *testing.T, cli *client.Client, qmReplicaIDs [3]string) {
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 4*time.Minute)
|
||||
defer cancel()
|
||||
|
||||
for {
|
||||
|
||||
21
test/tlsdifferentsubdn/difsubdn.crt
Normal file
21
test/tlsdifferentsubdn/difsubdn.crt
Normal file
@@ -0,0 +1,21 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDiTCCAnECFClOtyvBoQXVAGMcX0ObUawZJuYAMA0GCSqGSIb3DQEBCwUAMH0x
|
||||
CzAJBgNVBAYTAklOMQswCQYDVQQIDAJLQTEMMAoGA1UEBwwDQkxSMQwwCgYDVQQK
|
||||
DANJQk0xDDAKBgNVBAsMA0lTTDEQMA4GA1UEAwwHTVFNRlRRTTElMCMGCSqGSIb3
|
||||
DQEJARYWc2hhc2hpa2FudGhAaW4uaWJtLmNvbTAeFw0yMzAxMjUwNjQzMTBaFw0y
|
||||
NTA0MjkwNjQzMTBaMIGEMQswCQYDVQQGEwJVUzELMAkGA1UECAwCSUwxEDAOBgNV
|
||||
BAcMB0NISUNBR08xEDAOBgNVBAoMB0J1cnJhZ28xCzAJBgNVBAsMAkJHMRAwDgYD
|
||||
VQQDDAdNUVFNQ05UMSUwIwYJKoZIhvcNAQkBFhZzaGFzaGlrYW50aEBpbi5pYm0u
|
||||
Y29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAys3CaU4vXdZuKyw5
|
||||
AMkM4onEiJk3/TulWZIrXvFMgYLzAPX5eVUzs+eKfSBVIRiDGtsbmhcztT1GiAU6
|
||||
l8G9jHSegItJpKjiBEfFEuMmV6Fhx7ZjQdpyGdV+0bcE2IJHmeiaNxouvsV5gBJT
|
||||
vEamVsw9zU7GGOhhMyBQUUQDNy7yoHn8CBhDdoBskwJtpqPcxzohUDDt5wqSqOUl
|
||||
jo8yS375k4Q18hWzWIxJIAHoAFk+YyJqLq3mqq438Z9WSgjv9V+eLpNtKlge2IzW
|
||||
d1uH/siXE8Pp6p3WleG7cw2dzQZap+ekbx+3rRVLs8WYBHeTx08980sv6bBUKwHw
|
||||
aXXXSQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQAMrE0nDBw8wfPR8w7hrUPyG1Ib
|
||||
/k72yAv9wqaso1pL4IpXE3DFbuoIOQLjNCr+C7h2IzegRZ1z4kZbWh7LeES7M0io
|
||||
yDgM16Vikr9ek+NCLmF5QAtn/smhfhSEOjJoGkTPUTdWR4VdLeMFGQ9D8LHc0DFP
|
||||
EyPZy0JZQpRiXAs0ZEDhlFOCxI1aZzJhwGBJd9wOlG/SZKI8izC74mNPU1eE7Js6
|
||||
1sdU+4zs2wm/QtZ1MLlkKspSQqdNis/wpSSyjTEr9TkfzxVr4f3bALjQydkrcAyv
|
||||
BkATBYqvJYSHA3PS8VxNTDVef5EgKEWXlCmP/jfMcYNsUxBjaUcqiJJcmI9e
|
||||
-----END CERTIFICATE-----
|
||||
27
test/tlsdifferentsubdn/difsubdn.key
Normal file
27
test/tlsdifferentsubdn/difsubdn.key
Normal file
@@ -0,0 +1,27 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpAIBAAKCAQEAys3CaU4vXdZuKyw5AMkM4onEiJk3/TulWZIrXvFMgYLzAPX5
|
||||
eVUzs+eKfSBVIRiDGtsbmhcztT1GiAU6l8G9jHSegItJpKjiBEfFEuMmV6Fhx7Zj
|
||||
QdpyGdV+0bcE2IJHmeiaNxouvsV5gBJTvEamVsw9zU7GGOhhMyBQUUQDNy7yoHn8
|
||||
CBhDdoBskwJtpqPcxzohUDDt5wqSqOUljo8yS375k4Q18hWzWIxJIAHoAFk+YyJq
|
||||
Lq3mqq438Z9WSgjv9V+eLpNtKlge2IzWd1uH/siXE8Pp6p3WleG7cw2dzQZap+ek
|
||||
bx+3rRVLs8WYBHeTx08980sv6bBUKwHwaXXXSQIDAQABAoIBAQCdbqs7yij1BG/T
|
||||
ben2VRx+g4ogrCiNmY7bgJ/QfSrx4vC3TztR2DVhtB2K0t2i6n9kCrFbpiVKzX2C
|
||||
O+TnR8vYS/N7QCV0AHIr9nbjGZh7MFlSiqB0z5oBuf1P2W6WkFP7A1kr61RcXbnb
|
||||
FN8R6hpYiQZ06XDYhxRldvFClLSWUaZOhM6DoEcfNV5djDWPv4tSlw0ILvgIDfeu
|
||||
DfW5d6ih51CT+NqtXUZNgWB4WCFrqxLz2H197QtFNbeEnT2kqU81LiSR6ZLU2LAc
|
||||
SEhWD9xpldPvm6CsYDy9LQazG+F4KA45OGgiHUfB43EvGnDHJEeX4HFMFWYKmefa
|
||||
VQRD5GKBAoGBAPEQXruYnfYk3B+arl4GWCZd37TQd4ik2HybkhxPK/LXLJhuImun
|
||||
6gYu0Y5dSpBv0B6LjhTencAjgfZRSiCplxbnnPMg2Tuu1FAjG7W5UlKsT8IlmHR3
|
||||
3LqublsCQDaww/pBlXMsT7TUe89uH05v5Fn4pwp5Hy6IArhJLecDoYk5AoGBANde
|
||||
hrLVn0XhFwQWe11vbhclF9KtJHqspXhKX6mRcQ4VSzoGuaji+ISyfoCaU6kVQrq8
|
||||
WCZIOcTJTDScrk3BYQbY0+I6hBIug3SXhR8pGshQHvGOC/pAUInzWO3xc3lbJAwR
|
||||
aK70zN8wXFJjc1rOivMY/mRvdJSYlPmr2e5fJg6RAoGAEj97/FVsN7LImvfZlTKD
|
||||
v7vBcG2LbuOTo7MfF1eC6yoQrSVBI8cdNwSaRl2XhGGCbp1/zuKfLGlDsEKtCtXr
|
||||
owc7YUguSY9NcReHRHVX3vw+OWMhLEfahKMppWgBNmKhIzONvZ8wFW80RBqA8i4U
|
||||
Kh9hfbB3hM0074BSojcrJjkCgYEApavzVjJ6WRjzyZM5xwBm4asJDmlefHe+ujAM
|
||||
MrbNDxZWTgbKXx5qKjnckjUlUhYmxNsJvDknJzfqRTaZ5vpxFwFIzOhSnGHngZLl
|
||||
Nrk5/wmTJCIvGIzM57GooTFxsNLpgdcKfjuWNcJP4pjaLepgfOynFL+gIIbXYtBN
|
||||
zs6myeECgYBp2shsVXAo0G2Iw5qSrPmsC0WurVYv5jZSAcnbV468vgGKP+L65nou
|
||||
c7iuLWFJdsvgZUXWyijsQNmkOpNDkfYxcimFtNWMzt2IcoAEQzk5VO0Ok6EfrVCr
|
||||
S1Kj0bo7oOyy0eZfQKVs39gE+ZKc8Fn2s4w4l6ZayhTKcfW6lwrJJA==
|
||||
-----END RSA PRIVATE KEY-----
|
||||
23
test/tlsdifferentsubdn/myca.crt
Normal file
23
test/tlsdifferentsubdn/myca.crt
Normal file
@@ -0,0 +1,23 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIID2zCCAsOgAwIBAgIUdbPSj6WWhFu2amL9voKbyCMKiB4wDQYJKoZIhvcNAQEL
|
||||
BQAwfTELMAkGA1UEBhMCSU4xCzAJBgNVBAgMAktBMQwwCgYDVQQHDANCTFIxDDAK
|
||||
BgNVBAoMA0lCTTEMMAoGA1UECwwDSVNMMRAwDgYDVQQDDAdNUU1GVFFNMSUwIwYJ
|
||||
KoZIhvcNAQkBFhZzaGFzaGlrYW50aEBpbi5pYm0uY29tMB4XDTIzMDEyNTA1MjEz
|
||||
NloXDTI4MDEyNDA1MjEzNlowfTELMAkGA1UEBhMCSU4xCzAJBgNVBAgMAktBMQww
|
||||
CgYDVQQHDANCTFIxDDAKBgNVBAoMA0lCTTEMMAoGA1UECwwDSVNMMRAwDgYDVQQD
|
||||
DAdNUU1GVFFNMSUwIwYJKoZIhvcNAQkBFhZzaGFzaGlrYW50aEBpbi5pYm0uY29t
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwl9MlmCNG7kzk7qKGuBX
|
||||
jTONcn2OcifKfwgJlvzTqHcf8X/BIiyOOiwoeznke///LLtt9ygF0iyBMQYM/CG/
|
||||
rzOR6tbzI4y4Bmx6VqY02CkXi/p66ywQ7B5N2Fdp9Bop3SnthTcT4NoXBSEUhI1O
|
||||
ob8lDFv1KMRkCULD2sA0FUYCrHtw0M/vEOqsA9VVjyzOXsIlbbR1BSXtlWNneGeL
|
||||
OAdmQWO3QYCku/YrCyJlscvIisjp4s7guGnQh0Ws8h50R5sqag8RvdHUwExVLUfZ
|
||||
L1Od0+hCiO5mNfKekT0cs9owplcwgNHw88b8q4/aHDBtQRgsukkMxTpo00ftPJxI
|
||||
nwIDAQABo1MwUTAdBgNVHQ4EFgQUOyw89AeB0jXb5WCZv/5oDY8oWxkwHwYDVR0j
|
||||
BBgwFoAUOyw89AeB0jXb5WCZv/5oDY8oWxkwDwYDVR0TAQH/BAUwAwEB/zANBgkq
|
||||
hkiG9w0BAQsFAAOCAQEAtbybQQ9GpY5gH7xz4EWOUZ7XMmBYtuGXVrqUd+76hvCA
|
||||
H/SB0nl2bGp7tAKBttmXhfKVac6wFCbXvYe49B+Q9+iL7H9st9VZUPKLQ6K3Uet6
|
||||
L1ggMm2BhecpuYbwkG7ZidVFo/SuUCbCTnXBgHjvq4IkVCaJe7aKZmejSCh7gsIR
|
||||
BQkZvz/22Vx/WPTEYp0x/riIvSViBjLCuD25Y+nCtS8c2xGVBjs9Q4GWCOAvEfAr
|
||||
Tqs42brH1Vs92xS143p2h/wv52tmhfJI6X9QVQBBUoIjPR/VDFqZU5EYhAvuQPBi
|
||||
UADz9hNYGQ9wBzZGvzbrorpoT+7aW9nGtmUsvvupBA==
|
||||
-----END CERTIFICATE-----
|
||||
3
test/tlsdifferentsubdn/readme.txt
Normal file
3
test/tlsdifferentsubdn/readme.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
This directory contains private key and a certificate that signed by CA.
|
||||
The directory also contains the CA certificate. The certificate and it's
|
||||
CA certificate have different Subject DNs.
|
||||
68
test/tlsintermediateca/chainca.crt
Normal file
68
test/tlsintermediateca/chainca.crt
Normal file
@@ -0,0 +1,68 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIF5DCCA8ygAwIBAgICEAAwDQYJKoZIhvcNAQENBQAwgYsxCzAJBgNVBAYTAkdC
|
||||
MQ4wDAYDVQQIDAVIQU5UUzETMBEGA1UEBwwKV2luY2hlc3RlcjEMMAoGA1UECgwD
|
||||
SUJNMQ8wDQYDVQQLDAZIVVJJQk0xETAPBgNVBAMMCEhVUklCTUdCMSUwIwYJKoZI
|
||||
hvcNAQkBFhZzaGFzaGlrYW50aEBpbi5pYm0uY29tMB4XDTIzMDEyODAyMjEyNFoX
|
||||
DTMzMDEyNTAyMjEyNFowdjELMAkGA1UEBhMCR0IxDjAMBgNVBAgMBUhBTlRTMQww
|
||||
CgYDVQQKDANJQk0xDzANBgNVBAsMBkhVUklCTTERMA8GA1UEAwwISFVSSUJNR0Ix
|
||||
JTAjBgkqhkiG9w0BCQEWFnNoYXNoaWthbnRoQGluLmlibS5jb20wggIiMA0GCSqG
|
||||
SIb3DQEBAQUAA4ICDwAwggIKAoICAQDyTaomebXQXMGAs3ux3SSJnJseozpUIWS2
|
||||
eUMG8U3YK81raVpFsGPcjDi4RdCo72SqwD3LKKJmyfz4NOlKpZJq5rhqaHkECRj/
|
||||
GUKihl6Fr4OWlcJui/x4xeJmgFHvgnQEH/r8mvVVE8GqKHX9mRVOMaJtG14hm1qI
|
||||
DoK+x9IwOut/H5FMeici/C11xIDK65/54vztb4wEfyRNK5e8dFwuD9yJo2gYM0GB
|
||||
csQTq5WbKr0/uMF9rfFEx0lybHEASqgLCUA5lAGFtexefCFNxOxLnP4U2c5J+bcR
|
||||
rGQ/hpfw02m0UU+fuNba8GPJbXJ6zT+FP0kme0180OKmH1zcNDRJuGY6OQ1nICHc
|
||||
hf9QF95XQcwkf4MIltxZiNHbhSLuHrNFImv/AxiFchQJ+KPMLHV23x1uVKvRK3Mg
|
||||
ZOEZmtCZVTRbOF6AWSTMGblu1tuxjSohO9ycqk8yQk/YkHW3zuEiIV+AAZW6qMKI
|
||||
UZK64AoFb+E79gRpamz3ZOFfzNn/nKT4eMovDUUa8W4sVOVjBP2lT87xt7WBsE2f
|
||||
mK4vE19hQGMzI3jZcMjIwNp4HjKIAeZb8o21ywogGF0qHctoINnEu7nG2trH02ug
|
||||
0d+hwCfPRQHB7dicwTZm8ute4cHrwXed5tt1Bwv4Rq45GbwaOCAeZWa9S9GUaY/t
|
||||
9xeS66HLPQIDAQABo2YwZDAdBgNVHQ4EFgQUMIuRPon0lwjMxi5tLwJYRcagTN0w
|
||||
HwYDVR0jBBgwFoAUMjCCvlMnTcsiRvpiGPgBUGAA0EwwEgYDVR0TAQH/BAgwBgEB
|
||||
/wIBADAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQENBQADggIBACPpl2Im9El6
|
||||
m7T8cmtgXEW+rcQH/AA3px3+J/RnSWyRUi5fb0lc1RkfxQvuG7/sH3ywOc+uuB2b
|
||||
3lg6zkNaP4tZYTEjrrkj5qFycoQcvem/5nBKuQ18+ZGulHqE+YzcPTmbNO2VzD5e
|
||||
RJJpboiJa5TtM4wBeIuWbicWN+2wPeOkiQHxHEWtXOEVmbKzuREI6JHg2CDYMpWb
|
||||
GWqS3hn9hfQoNhtSdtDFGChlsTpgKB/gRpYEfrlLKYKekJafD+xhEl/ZC2JDOtUr
|
||||
/dZW4JicC++rUrMt89aFzEAOtcaR4dbL4NQhMRAlF9MdjZvHdcJqYEd3wqOymEnV
|
||||
4ce1VpraZIh3qywqoOqf54vjd5DugpFoxjye+5ynzfMGX6RjwWnVRiY36kjFiMFR
|
||||
iGhFT74lbRmw86RUQFjiXWAG9R0cR5s0EyOM3Egp/qRkWxbG6z7JeCdUd7Z/aaR3
|
||||
iT081PUcQsl+jzCw2J6SDkemzZn/spkViodfzGJ5xEXB0fldIDarbDqOeWjSoKaz
|
||||
LA7qIfPJptJkwybK/hBqvfR5fXx1VuubJdMfbo5jmP4OVCkNV5I6D5EMkYCxPrOl
|
||||
9eVjYbndAVAXRF8vopJRqe067c+IwKPTnG4iIoXcR28efCBQ0Tv8DXYmHjSlVLdt
|
||||
UCqYhFBxgGOUJLmqe7O9TWVk/aIM3Q3M
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIF9jCCA96gAwIBAgIBADANBgkqhkiG9w0BAQ0FADCBizELMAkGA1UEBhMCR0Ix
|
||||
DjAMBgNVBAgMBUhBTlRTMRMwEQYDVQQHDApXaW5jaGVzdGVyMQwwCgYDVQQKDANJ
|
||||
Qk0xDzANBgNVBAsMBkhVUklCTTERMA8GA1UEAwwISFVSSUJNR0IxJTAjBgkqhkiG
|
||||
9w0BCQEWFnNoYXNoaWthbnRoQGluLmlibS5jb20wHhcNMjMwMTI4MDIxNjIzWhcN
|
||||
MzMwMTI1MDIxNjIzWjCBizELMAkGA1UEBhMCR0IxDjAMBgNVBAgMBUhBTlRTMRMw
|
||||
EQYDVQQHDApXaW5jaGVzdGVyMQwwCgYDVQQKDANJQk0xDzANBgNVBAsMBkhVUklC
|
||||
TTERMA8GA1UEAwwISFVSSUJNR0IxJTAjBgkqhkiG9w0BCQEWFnNoYXNoaWthbnRo
|
||||
QGluLmlibS5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDjTlg8
|
||||
0bZ3vzeYLJ1Z4zp/HplJSDzAn22EROJtHNb1pyfWfaO7UyUy5jNt1UJ6hTXy1xOS
|
||||
r9eGaSu3kAm/R/METYgsMM56ALQ458VlLpZUEJBMgu63W6/FTckl5iUno7n9y3qL
|
||||
2CKkHuEXG9jd/jPhBX+GLc+h1iwe1URaJSe1CLdSc8SMsGZUC3wMO3EAAqEOHh9j
|
||||
BQy2qeWpR6oAF8HST5JX6lW+k/4NeNthdse8/Oqv1otPzseXE9jz09tz+4Qd5fdt
|
||||
J8UcCWPcpAFb/TM6S2Hpr8xSqZr5+Em4JimdNDtEbJZKjzSwvmZ4S3qsUP0xb9ad
|
||||
NgDjxYjBqNStgjrSzKGebazD6U4EDa8dXS6UxqgnxmtcSQLXIPcwfH9FniKnR7Tu
|
||||
/cmSDOdWCedyxeDjrLlGuFUtJiSXCqRHU8c5KxTVkyDV1YZPjISktVJQS1/n39Lf
|
||||
2TtHNTH3qTDfPzcgBqqczF+vlIKspE4YcTFBynOlL4biWZEsddOjpoqzamycMBqj
|
||||
sEMlvydggS+Z8oZqDFVTU9/BCC2mth9HZr7hjO950IQcgVKexXOqxgPP4rmzEl2k
|
||||
jEp9PKdDqBv7lOYjYO4taegfCZs5FPwaKtCStmzQLERMwSifqXe0TGV5obA8asRY
|
||||
pOiZf6d9nVBvIYKotB1mNJTMqBke5hom8uarPQIDAQABo2MwYTAdBgNVHQ4EFgQU
|
||||
MjCCvlMnTcsiRvpiGPgBUGAA0EwwHwYDVR0jBBgwFoAUMjCCvlMnTcsiRvpiGPgB
|
||||
UGAA0EwwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcN
|
||||
AQENBQADggIBADYAHeWqQEg+oIC5qy8obEul4Zuk5J9HignrP8ef0+wdJf+Zx9/j
|
||||
3nZKBrUrgZiBP+RJgNYLRByO9QT/q+P5dLfuLikyP377oLlVUcXQIXbJU60Yxdl5
|
||||
wrpS9O8nfbwMUdi+1foGmA17zYNiTfBZ0/vem6yrhKdP6pS4h1+291tF3+mac0Q1
|
||||
sx7RhssZT22YclH9u3U4q7Ef3q5ilqV5fb/Rm2z/iGtOLF2swm/jS1CEpJbyb0EE
|
||||
yF8hoX8oliJdunmUqGV8bK5D7GFx47tyS5lU4B/Q14xBYUUc4hs83xAs667h0fym
|
||||
0fbyQGh92bASPEDw6ep8eI6Iv+DSniwEFPG0ayDtnSEM/HkRVML497Rhqks/cP/9
|
||||
rMJe9hUoetKO+p390R22EQLqGT6nvWV1jsGMHIMjJB7kfBLbgvFz8pQQdo7IxK4N
|
||||
IirFdEn/atri2Z6baT6eqKt2tnjodQs1F+14rK1cj0QbH/tkHF8S411MclmufthB
|
||||
U+VlEcjr4pnpVzvE4B78kXtlRH1N7Pj+vIgwpiC+hnWcQdqVF58E+7ERz9U1JqaQ
|
||||
tX9c6aZW4m1pMcxR3c76NnYJi6rmhoL8xUNDq7HXrkK3DM0/rUvxl5HwfgGC0FGS
|
||||
QmFgXgNddo2pj/TjnssE9XxFjsU+gEbGudGe0p5HR5qlKzTLc44WxcmW
|
||||
-----END CERTIFICATE-----
|
||||
34
test/tlsintermediateca/ibmmq.crt
Normal file
34
test/tlsintermediateca/ibmmq.crt
Normal file
@@ -0,0 +1,34 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIF7jCCA9agAwIBAgICEjQwDQYJKoZIhvcNAQENBQAwdjELMAkGA1UEBhMCR0Ix
|
||||
DjAMBgNVBAgMBUhBTlRTMQwwCgYDVQQKDANJQk0xDzANBgNVBAsMBkhVUklCTTER
|
||||
MA8GA1UEAwwISFVSSUJNR0IxJTAjBgkqhkiG9w0BCQEWFnNoYXNoaWthbnRoQGlu
|
||||
LmlibS5jb20wHhcNMjMwMTI4MDM1NTAxWhcNMzMwNTA1MDM1NTAxWjBHMQswCQYD
|
||||
VQQGEwJJTjELMAkGA1UECAwCS0ExDDAKBgNVBAcMA0JMUjEMMAoGA1UECgwDSUJN
|
||||
MQ8wDQYDVQQDDAZJQk1JU0wwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
|
||||
AQCvn7rGrAzGr1BI688hirHC+P1xcI2dUAc1EZx9bQ1mzSAP+9F5hBzT0Ty6ay0Q
|
||||
zs8qKOs9YpHy1qCrETlrFJxWmd8IYVhT2QoKPxF6jhfYMf6anabtYSRZe1c3v/zi
|
||||
DyuLuS2XuDeHnfuJl732YTtteYcG4nAx2Y2GcKLnEfNyrB49+ZGbjnNTqIBsR1tD
|
||||
U96C1h1z4PGtZDxDGbdjGlaB1AJXH9r2tocaS2WyBKo8w9ogTI5d63NYdVvsPKm8
|
||||
AKwnVLu1kE/xk8/5fraW3tyX4JmCFk4Tt/rf+Oy0dbFfoC9m6JmHPeEopqE9f/qy
|
||||
m0WpqM3PHuz2Kke7RQ3GNYFDAgMBAAGjggGzMIIBrzAJBgNVHRMEAjAAMBEGCWCG
|
||||
SAGG+EIBAQQEAwIGQDAzBglghkgBhvhCAQ0EJhYkT3BlblNTTCBHZW5lcmF0ZWQg
|
||||
U2VydmVyIENlcnRpZmljYXRlMB0GA1UdDgQWBBTaihsw+XFiyTZXn27Y7qpTFQSD
|
||||
2DCBuQYDVR0jBIGxMIGugBQwi5E+ifSXCMzGLm0vAlhFxqBM3aGBkaSBjjCBizEL
|
||||
MAkGA1UEBhMCR0IxDjAMBgNVBAgMBUhBTlRTMRMwEQYDVQQHDApXaW5jaGVzdGVy
|
||||
MQwwCgYDVQQKDANJQk0xDzANBgNVBAsMBkhVUklCTTERMA8GA1UEAwwISFVSSUJN
|
||||
R0IxJTAjBgkqhkiG9w0BCQEWFnNoYXNoaWthbnRoQGluLmlibS5jb22CAhAAMA4G
|
||||
A1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcDATBaBgNVHREEUzBRgh1b
|
||||
RnVsbHkgUXVhbGlmaWVkIERvbWFpbiBOYW1lXYIXW0FueSB2YXJpYXRpb24gb2Yg
|
||||
RlFETl2CF1tBbnkgdmFyaWF0aW9uIG9mIEZRRE5dMA0GCSqGSIb3DQEBDQUAA4IC
|
||||
AQCD3iROwkBiSfJ1jCzUdblYawclZE3kX7remHR77sAuGYHEsHhU4PmXUs+A70JJ
|
||||
jF8gzc1cqMqy4Kwd1BGbNLp9cdtre4TigQ9UqbqxCENyoq0aTIhHmJ5GP3RKMwC1
|
||||
jaNmH/MUlFhOKZsTLKymkBGCA9GLhD+quU4AIQHMLGoxMIbRZwZzyuGpa7/Gl2Om
|
||||
f6taMfBsnmFFC+O+saGvu8TG+Q28bGA7wJQM5WMxyVbVY2Lkbb4u2/gDEY1/6T7g
|
||||
ZkGuCxVlyQ2+dy5teKe7I2AGkgTbwl39i4YMGdj9ZC7ydRIANnNgCuygUTZ3c+w3
|
||||
PvA33cX7ICWrmrk0y8Ulox7uj1jNi/npdwPkfjyuh9fJpdV4J/BcsQyZ/j4F5Z4B
|
||||
5MrQeJ7wEELYDv1OOTntyQoqH1HH3TrZ3PFS0whA6gTT76ci2ra85vLVW4SJrbKj
|
||||
VvDr5VcHE+IsJBedscbP2fO6imkAB74xdkBx9uy7x4aXJi399DHvw6b7mMsbR0om
|
||||
6CpI0akjprfhdyv4Ri3vvWpWrzHMUMjzLuj6HYopBlFLErMe6WTY1BAh4ljUFKZ0
|
||||
141/BCkGzNpH/5g1O1QwdQXzEIgUjG16Dm7gM2WKAeJFBttogX2ygTRnVRCZs/fY
|
||||
JE3CwtxKczC0XteonH/ylGTQQR/0J8Y/ozLAWQxDyzi2Og==
|
||||
-----END CERTIFICATE-----
|
||||
28
test/tlsintermediateca/ibmmq.key
Normal file
28
test/tlsintermediateca/ibmmq.key
Normal file
@@ -0,0 +1,28 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCvn7rGrAzGr1BI
|
||||
688hirHC+P1xcI2dUAc1EZx9bQ1mzSAP+9F5hBzT0Ty6ay0Qzs8qKOs9YpHy1qCr
|
||||
ETlrFJxWmd8IYVhT2QoKPxF6jhfYMf6anabtYSRZe1c3v/ziDyuLuS2XuDeHnfuJ
|
||||
l732YTtteYcG4nAx2Y2GcKLnEfNyrB49+ZGbjnNTqIBsR1tDU96C1h1z4PGtZDxD
|
||||
GbdjGlaB1AJXH9r2tocaS2WyBKo8w9ogTI5d63NYdVvsPKm8AKwnVLu1kE/xk8/5
|
||||
fraW3tyX4JmCFk4Tt/rf+Oy0dbFfoC9m6JmHPeEopqE9f/qym0WpqM3PHuz2Kke7
|
||||
RQ3GNYFDAgMBAAECggEAPeL1eEjsf58LlYazCMjM9z2yYaUd4g9vWr4H/RLOpCko
|
||||
YTmFiWKKngGfermFueSGj/63VnxDneUP3PhG2Xr71HCIbXWQIIvcw9uRlzQ3JtIH
|
||||
PAjN59xRaM7T3ytiO27JE4V/kXUy7DE5kDTOleGRhXRLpptomchl3LgYT4C93uw+
|
||||
Pul/KS4PZrGvTD+QnKNd4aOmlEaVLDRcwMqvg96bofE4Qk8LniQB1xEuYUpJFtPm
|
||||
dBaGmtkTg8M6ghqOYMMqx8qNjqcv/HxIUFbV6F6fmhDOAAvt0gRzhHsmJieYiWFv
|
||||
Igj/pmJbo/sfcgiiV4g3lEfqP4i9WpMmpVtEbOrvUQKBgQDio+YfKOwO4Pv0soi6
|
||||
4qPpAZyYvDeQNFYfm0GP/FBuFHUR7dMA88snPZUY8mSgtfG/zoQdcyI3G7HEsugD
|
||||
redwGoVvp0s9435xW7KCB3tMP3PhmeMjXihQw5cXyJx+LrHjorme5zQ4OkcB23lF
|
||||
cjN/Yv0ZxRp2wWpOp3F08CvntQKBgQDGX/h3DTx4IHRjfL6jnL8vKJIsxNFhy/Rz
|
||||
SMMnwAXLmYv29O+rupqsx/MNbM/VA99HqGQt8p2fVEGafYlolaFjICZM6DH5j30B
|
||||
t1M4LsPZf+fI5vWI9KHwHT1JxCqfqM5GwwHMrEw8pdicr/+FS6O16Kymln/aAILb
|
||||
sjvpFyLwFwKBgQCO1m06RjhASFuDJOI3po9XUsS3HiiGofWFhfwUGxk1x37hBdpu
|
||||
RzhKSu2lA1+YShNKp4VsahuuT64CIh9H8lpitNRUQkORhccy+m/Os5hpvbPzA2G7
|
||||
8KPIAv0+6Bh5DkTfCreiBmVK6q/F4+TSd98s8d5CV48OOWgemjlPUe7Z4QKBgQCu
|
||||
MiYP/NqFrhImLquFJqantZuunmIy25NcDJ/6bt9n6vyCLpGrniAm6yneNxfFuTG/
|
||||
TfoycuLAv48gJ26bHRHr5pZbYGZJ/BtMf3wfUMmAW5Xg0Bb6Xb86B6MC/LRlISmJ
|
||||
78HLxdzoQMYWyWG63jHzEk9RtcStXVeLrlZ3l26BnwKBgHxMtBHvcaXWzRdm1PZ0
|
||||
2DVFOP5lm/5t5WC8re8L9oeVLt0+yMBEKXUwX66Z1s5iO24CWsHFN8zDjQhVKfZM
|
||||
/YOxbfjQWYO1LsgJweAEeEauYI+ncMnQEZf1Ei1P8g2X1GTS+n0r4YANawWczp71
|
||||
tZURwKJSJwKpGMChxucQDJc9
|
||||
-----END PRIVATE KEY-----
|
||||
3
test/tlsintermediateca/readme.txt
Normal file
3
test/tlsintermediateca/readme.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
This directory contains private key and a certificate that signed by intermediate CA.
|
||||
The directory also contains the intermediate CA certificate and it's root certificate.
|
||||
The certificate and it's CA certificate have different Subject DNs.
|
||||
30
test/tlsinvalidcert/expired.key
Normal file
30
test/tlsinvalidcert/expired.key
Normal file
@@ -0,0 +1,30 @@
|
||||
-----BEGIN ENCRYPTED PRIVATE KEY-----
|
||||
MIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIxuHuzG+WcZACAggA
|
||||
MBQGCCqGSIb3DQMHBAgMKyhajZd/ewSCBMiCL57/BUvVwtxmhaIqQBSjHLnx+AvG
|
||||
f+frFkwM52KuAg0Q+vF/1iQXMbZRrHpxNz/1t3cIdwzlc9S3FB+L42q0CEH0c1FW
|
||||
6gB6csmb1AyhGYffwkeT8bPkELJhpO2taIFTynwDhHaeKeDce3EwQ5xQd4ydq1Ku
|
||||
t4oJZun51J5qvNs91BiHHVB3/q1Lnh4VK4TTDgXTGfu8qcuOexHQcOgko4oemorv
|
||||
E1Woy6Lf99bePxedSuMIxPAXysSCBqcrZDbY9K3yaxV6Gr7S9FBVvOx9r8PTaFEY
|
||||
sXxdtB22qM1PqZIIjK6nzwXb5iu8wcaZFatD6y7RQaS0kz5EcS5kExrEocFV7D1T
|
||||
dtTf6tt31PbtKfEauRjz3fBXMw+r3Pu/hWRXd3og9hfzFQz49v9RdvgkHy57uCY+
|
||||
F/fN3BKyTcTqJWKpBDMx/xtD1kXWHf81SFmckuShbgKjDvZraR4RmzO/9upIDCFF
|
||||
rJ85takmPvZyAdfvZp6RqVja+cphI4nmxVvmOmUiFMmUJ0lv9MhqpxdVFfleWz0w
|
||||
9XxeL4l+cd/Kbl2ihHwF4oOzWlamyRZjoWiq5s/ika9I1iQ40rGTThqnphonlhUz
|
||||
HX4NvA+fbugwqyOjqEkg5AqyP5ucUN3Hl0qd7wqAJprDkB938unKo/1sr82PfdO/
|
||||
5UvzsKz2ieVavEzi/NxYyR+Xjm3YHG1akcftvElcpYepvemt/PtXJH8fX29b1X6C
|
||||
TYxV03MeA1sJXeM9iqIf/wGQJ657U5ciXUyUtU8s0dDY+jqpZVjxekgCove9FNXz
|
||||
Mja15yiAy/F8xKX20ZRisg+Ly60UQYbX6Wu6Uy6NMjWkxZOYebCVYLMag6UjS0CS
|
||||
8n3/DNvuVooGWbgBFs0tyabtlz/Fz5jGktkMBQeEK4PZKZdMt9aXnDfPFGJWrsme
|
||||
bUzEZLZSWD2Zwr/ujzfBgVeg56AyHJzHuOOLaofZ90ecv69Udl7sYZBelFXxgN8R
|
||||
JVXyV0kNZYj9LaqRTi54H8YToAIV7GXIcasH3jP54dOba3px2NXbaiMf7Oe8apyW
|
||||
E7/vKQNoIbWaglRHXVPGoATTUzYNdBO4jca1rTOXjmzsiZl1JGPwQsS03PuTfa8C
|
||||
F7Uqxj8P9m71G1KuLYA1es55aNXzXSx6uqrRYeu0iug/jE+voGfjbRW3BUY/qxtl
|
||||
OjR8pCDPLN6/rt//ejvBdA4gPDgjRNH4fO9DULQDqIVHmYlysZqhbPbhzmiBXZVP
|
||||
zw1/z9amWR00OeN7xUvm9n+65dosoIn2v1dOz8JBh6Uooj63D2cOghBwEXoPg53B
|
||||
3vLgqy3NKyNZQ/gGfBjXTTeoWzAy8U8hKecNKcsTBquBeBUfS55WwkbD7Mz0Deho
|
||||
EcMZPZUwAPyJ2kHL74etpoBXuH7Jzo8SVedhE+C5J6F+oUP/JcoXdehdGLI89CHU
|
||||
cUARt91OiF3WQo51xT21GlGL8RMHVpjTnYZinnQ/fE1y7JfWtYshBya+YLlAk5vb
|
||||
9jt0tZ9I/KhJ579ZwWBH8V179kBr5Vn+uWlEBBaJu0abdbRSDjt6fAaxi3Q8XK+c
|
||||
isuVuzN/8JAeRgRpm7hRfIoG6XOsrY72ktz0JiHflZ+90xaPRvpCA9/mFC5qp1zh
|
||||
yBs=
|
||||
-----END ENCRYPTED PRIVATE KEY-----
|
||||
BIN
test/tlsinvalidcert/expired.p12
Normal file
BIN
test/tlsinvalidcert/expired.p12
Normal file
Binary file not shown.
26
test/tlsinvalidcert/expired.pem
Normal file
26
test/tlsinvalidcert/expired.pem
Normal file
@@ -0,0 +1,26 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEaTCCAlECAQEwDQYJKoZIhvcNAQEFBQAwgZ8xCzAJBgNVBAYTAlVTMREwDwYD
|
||||
VQQIDAhOZXcgWW9yazEPMA0GA1UEBwwGQXJtb25rMTQwMgYDVQQKDCtJbnRlcm5h
|
||||
dGlvbmFsIEJ1c2luZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMTYwNAYDVQQDDC1N
|
||||
USBMaWdodCBFeHBpcmVkIENBIChmb3IgZXhwaXJlZCBzaWduZWQgY2VydCkwHhcN
|
||||
MTUwMzI4MTMwNDI2WhcNMTUwMzI5MTMwNDI2WjBVMQswCQYDVQQGEwJHQjESMBAG
|
||||
A1UECAwJSGFtcHNoaXJlMRAwDgYDVQQHDAdIdXJzbGV5MQwwCgYDVQQKDANJQk0x
|
||||
EjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
|
||||
ggEBAMeNfSaC0XZoi9CHXLUHz7BA8frIlz+gnl1sjaGimsofiMjd8a0c4pOhlusR
|
||||
kvB4qAQDxmDYhh9QTuqiJZvQxzTI3sNt4W3kcm/bEVawg8HvsWBY40fKkWJYlypV
|
||||
K4Oizcri7CLaeUwgpJlrSlYiJSzjaaytTDeC1F+RSTrdDg0CxI+pfhxIkc9+HPMC
|
||||
oSv1ynxktcJPnCLGRIIHYCE4jm1ZsYxAkSV1crja5OQdd3czEMz6wfHdYzTJUEa1
|
||||
pmnTOa9lhuxlYb8ykt7G2CK66tXaoN0SsAsy6sT2SblI16RDkyOL5XJk56ThhHrn
|
||||
XjjWr3Zo4/tE/pgn5fd+91oi2+ECAwEAATANBgkqhkiG9w0BAQUFAAOCAgEAZNZn
|
||||
OJVQ54NLCzCyTiTauG2jJU+3aYEG4hRhpLM6N11vrukIFTeVfbrr1uKp7sLHYB0E
|
||||
6xLgTxhKF2lDHzCPjA3bOO0tDsxwkOZNP//jmMi+9VKd6Voh/UENOVnBHaXLb5G2
|
||||
9OXi70W/K8eQU7Qi+tu+snWNLYHb5nsYKnPed3+h/zLV3iNB7cz2DCNCas2cKekx
|
||||
6hJo+tWoC+jXcpM97pnqY6saYNmKmugIdZ1W77jueoEIIkSMAZvd8sgXzE6Ad8mD
|
||||
bTMimX9ri0APgxZewaF51YzR2yKqgkpvLVBef0nCbjCDK5zgJVSQHtVJswE4mIcL
|
||||
J3PbSxtmt3BcIr9jmYZZjmoXSjK0+YQkDYT9/uiE5h+6KXrk4AOTkcO9kuxQcBAD
|
||||
QYJB8Hr7h9OcWmpOOJDjOz4BspBwQ7Vs2swHBQgWu9mS9I187JFSFm46dp9CnqSV
|
||||
K5i5amsrJUcRtqlBs4JzqsLivUaet+BtQwlWDfl52TgWpWeIs2EiPFERZxiEggVr
|
||||
pIhjZ9F2d0pL1Oj5jCWMRGWrz1px5W4r92uzfYvTrT/eyGNGGNdDeAgidJ4SftXu
|
||||
XMcqgVjAy0kKvQEK9bCqfoOxTllFqLcneaPI6O0hLREph6sti6eDvJeMsUGoYm/7
|
||||
MxfCaqBU1HStl0HHPaaqHnPP+FW+/xuXhHij1hY=
|
||||
-----END CERTIFICATE-----
|
||||
BIN
test/tlsinvalidcert/expiredCA.p12
Normal file
BIN
test/tlsinvalidcert/expiredCA.p12
Normal file
Binary file not shown.
35
test/tlsinvalidcert/expiredCA.pem
Normal file
35
test/tlsinvalidcert/expiredCA.pem
Normal file
@@ -0,0 +1,35 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIGEzCCA/ugAwIBAgIJAI+sAC+/ups4MA0GCSqGSIb3DQEBBQUAMIGfMQswCQYD
|
||||
VQQGEwJVUzERMA8GA1UECAwITmV3IFlvcmsxDzANBgNVBAcMBkFybW9uazE0MDIG
|
||||
A1UECgwrSW50ZXJuYXRpb25hbCBCdXNpbmVzcyBNYWNoaW5lcyBDb3Jwb3JhdGlv
|
||||
bjE2MDQGA1UEAwwtTVEgTGlnaHQgRXhwaXJlZCBDQSAoZm9yIGV4cGlyZWQgc2ln
|
||||
bmVkIGNlcnQpMB4XDTE1MDQwMTExNTkxNloXDTQyMDgxNjExNTkxNlowgZ8xCzAJ
|
||||
BgNVBAYTAlVTMREwDwYDVQQIDAhOZXcgWW9yazEPMA0GA1UEBwwGQXJtb25rMTQw
|
||||
MgYDVQQKDCtJbnRlcm5hdGlvbmFsIEJ1c2luZXNzIE1hY2hpbmVzIENvcnBvcmF0
|
||||
aW9uMTYwNAYDVQQDDC1NUSBMaWdodCBFeHBpcmVkIENBIChmb3IgZXhwaXJlZCBz
|
||||
aWduZWQgY2VydCkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDLGn4P
|
||||
97BZteNO/5zIlGH4F067TPlOjFiLI1TFI/bKp3bx1fHpsQdMX5C9y1s6BqoOzMJV
|
||||
VHDJH4NlxvnMUGNzYvluynjujejhyDryDrb4uY3JMcewvnHmrWArzbfZw4eBzANr
|
||||
feBkro4xr6m5HWMxM4GJZeFaEGqbXNE5vAYhZ270qUKqrkSHv4g/LqapITaBPJDC
|
||||
3tAtV0UD4/HmwB2ogZlXy4DkbBsgcc2rS4xPxx3Qki78DeA9oT6aeLP7KZnoEgo9
|
||||
Xqb/UfRuZ5MYR17u8yTuJz/JdAs6Saf80t5PLDO/SRKj7Uu1lt+HbkpEghdEdB/V
|
||||
pPdmlIfo7uNe7rFG/OWif2qwVm2t9o02oIUqlAHggS4WYeOmcB8L9EtmlfZHaoDA
|
||||
H0a0xlbZ+XlE/EMpphgrzSE5g9h9Vw7vvH5Ygzks8lSXKd5VS8C9y8tWqaMCynAy
|
||||
5MTIZdXMvouG8vy5Ip+iWck4QSo6eJBryLNt/vzljxo6XntveXRZVvywyjh3NuVu
|
||||
nVLPe0CFkmRHXK5/zA6H8rZeGb3eK8Y4VcLKj7x4Lnluo47BeGnKH7UcKBA2AREC
|
||||
VGFVsHcr0HwKgCCSsyv4qRefY/mJWO59BL38shHuzydLxlKHf/uPGdsZ3DU0rb6i
|
||||
QXMCHC6lzAjJHVCDJ2witiPSjmW2LRZzRqdTqwIDAQABo1AwTjAdBgNVHQ4EFgQU
|
||||
dv3XdSdsOkZJDWgusfuZqBx/jpMwHwYDVR0jBBgwFoAUdv3XdSdsOkZJDWgusfuZ
|
||||
qBx/jpMwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAgEAR9o32FR4AjTZ
|
||||
LV5nH5rsQ9OVon1TCv9yemAI4coh08RHWVf7cDyIp8XKy3gTM/lq70wzsXUHwaAS
|
||||
4xSOjc/fBLP0UTEwCrdyy1guRs9ZjKIhqth1AkB92sF9HV4P811vUNppVfA/BDmb
|
||||
Ttl6i1Joyl6nhnDxn1HzjcSBB4ZVt/1MRwIaEfRXrqAklYbMOtw8D6Onth6IQ7dS
|
||||
27ulUPD+AA8H5Ilm2XhPI6ttnR+822+mgB1K9WAjmuIbDJwQJl32UmTUiXPwdlWm
|
||||
KqyKZGST8wV6Jylha84ETL99IlJsxoAMGmNshMe4t773n0LousAvnbVt9uZcBNUH
|
||||
d/53EWP7kw1PPhY6jo0gLVK/ZQt8MHBJuT3f5IM9dmkLJmyM9t8tEmvjNEoC2xSW
|
||||
JHxoWfbMOxxFx+S5CwwcvySpKltio7s5bf/dYexE+Dv/+zMDeMb8GXv811TCqsD2
|
||||
lm4kTsHjwd+zSGCuvH+R4vWbkSrd65CATmFsVpVPLsIVXDa3DrgFQBmUI/q9UFaG
|
||||
K25HztaHk8GJkBZdBA7xXxzSJxW37yqPGKqQ/ZoqSz6XFnsgy5AsW0fDM+cPUl7l
|
||||
tftOlLeSqkaBq9O++76yY2asb0AyqjNs0tajluzxGFZ38DOqA1xkltV/c+KYnEv3
|
||||
q+K4agMHIKMr7lGJfSBevz3mG+NJ7AU=
|
||||
-----END CERTIFICATE-----
|
||||
23
test/tlssamesubdn/myca.crt
Normal file
23
test/tlssamesubdn/myca.crt
Normal file
@@ -0,0 +1,23 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIID2zCCAsOgAwIBAgIUdbPSj6WWhFu2amL9voKbyCMKiB4wDQYJKoZIhvcNAQEL
|
||||
BQAwfTELMAkGA1UEBhMCSU4xCzAJBgNVBAgMAktBMQwwCgYDVQQHDANCTFIxDDAK
|
||||
BgNVBAoMA0lCTTEMMAoGA1UECwwDSVNMMRAwDgYDVQQDDAdNUU1GVFFNMSUwIwYJ
|
||||
KoZIhvcNAQkBFhZzaGFzaGlrYW50aEBpbi5pYm0uY29tMB4XDTIzMDEyNTA1MjEz
|
||||
NloXDTI4MDEyNDA1MjEzNlowfTELMAkGA1UEBhMCSU4xCzAJBgNVBAgMAktBMQww
|
||||
CgYDVQQHDANCTFIxDDAKBgNVBAoMA0lCTTEMMAoGA1UECwwDSVNMMRAwDgYDVQQD
|
||||
DAdNUU1GVFFNMSUwIwYJKoZIhvcNAQkBFhZzaGFzaGlrYW50aEBpbi5pYm0uY29t
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwl9MlmCNG7kzk7qKGuBX
|
||||
jTONcn2OcifKfwgJlvzTqHcf8X/BIiyOOiwoeznke///LLtt9ygF0iyBMQYM/CG/
|
||||
rzOR6tbzI4y4Bmx6VqY02CkXi/p66ywQ7B5N2Fdp9Bop3SnthTcT4NoXBSEUhI1O
|
||||
ob8lDFv1KMRkCULD2sA0FUYCrHtw0M/vEOqsA9VVjyzOXsIlbbR1BSXtlWNneGeL
|
||||
OAdmQWO3QYCku/YrCyJlscvIisjp4s7guGnQh0Ws8h50R5sqag8RvdHUwExVLUfZ
|
||||
L1Od0+hCiO5mNfKekT0cs9owplcwgNHw88b8q4/aHDBtQRgsukkMxTpo00ftPJxI
|
||||
nwIDAQABo1MwUTAdBgNVHQ4EFgQUOyw89AeB0jXb5WCZv/5oDY8oWxkwHwYDVR0j
|
||||
BBgwFoAUOyw89AeB0jXb5WCZv/5oDY8oWxkwDwYDVR0TAQH/BAUwAwEB/zANBgkq
|
||||
hkiG9w0BAQsFAAOCAQEAtbybQQ9GpY5gH7xz4EWOUZ7XMmBYtuGXVrqUd+76hvCA
|
||||
H/SB0nl2bGp7tAKBttmXhfKVac6wFCbXvYe49B+Q9+iL7H9st9VZUPKLQ6K3Uet6
|
||||
L1ggMm2BhecpuYbwkG7ZidVFo/SuUCbCTnXBgHjvq4IkVCaJe7aKZmejSCh7gsIR
|
||||
BQkZvz/22Vx/WPTEYp0x/riIvSViBjLCuD25Y+nCtS8c2xGVBjs9Q4GWCOAvEfAr
|
||||
Tqs42brH1Vs92xS143p2h/wv52tmhfJI6X9QVQBBUoIjPR/VDFqZU5EYhAvuQPBi
|
||||
UADz9hNYGQ9wBzZGvzbrorpoT+7aW9nGtmUsvvupBA==
|
||||
-----END CERTIFICATE-----
|
||||
3
test/tlssamesubdn/readme.txt
Normal file
3
test/tlssamesubdn/readme.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
This directory contains private key and a certificate that signed by CA.
|
||||
The directory also contains the CA certificate. The certificate and it's
|
||||
CA certificate have same Subject DNs.
|
||||
21
test/tlssamesubdn/samesubdn.crt
Normal file
21
test/tlssamesubdn/samesubdn.crt
Normal file
@@ -0,0 +1,21 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDgTCCAmkCFClOtyvBoQXVAGMcX0ObUawZJuX/MA0GCSqGSIb3DQEBCwUAMH0x
|
||||
CzAJBgNVBAYTAklOMQswCQYDVQQIDAJLQTEMMAoGA1UEBwwDQkxSMQwwCgYDVQQK
|
||||
DANJQk0xDDAKBgNVBAsMA0lTTDEQMA4GA1UEAwwHTVFNRlRRTTElMCMGCSqGSIb3
|
||||
DQEJARYWc2hhc2hpa2FudGhAaW4uaWJtLmNvbTAeFw0yMzAxMjUwNTM4MzBaFw0y
|
||||
NTA0MjkwNTM4MzBaMH0xCzAJBgNVBAYTAklOMQswCQYDVQQIDAJLQTEMMAoGA1UE
|
||||
BwwDQkxSMQwwCgYDVQQKDANJQk0xDDAKBgNVBAsMA0lTTDEQMA4GA1UEAwwHTVFN
|
||||
RlRRTTElMCMGCSqGSIb3DQEJARYWc2hhc2hpa2FudGhAaW4uaWJtLmNvbTCCASIw
|
||||
DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJNAGirCyLzsHpV2cyso8pc2BOP
|
||||
4j6lu2ZTsdSE+WVZsqX3YWmV8TCYpJQRD4jmlrYIEdYAGmKPZvbvWjhzZWOJIxfI
|
||||
MGRwQ4dl8bSDZpcc7OAcHgLaW1OnMy2/1NgVyyXOKTaG4PmzZCzSKnhDF8/+FhUm
|
||||
rIYi5EU6bn1MuY34HOswAau0G7YVeroBUPs+LUjUpQx4vp5Baz9sWBzWcvsZsoSo
|
||||
LTMWEqCocqYOhj1ALrwN61NzdRwVtoS41dcjHAiFKEVTKIbqE0ib57Of1sohO9dr
|
||||
JgjegBNi18lsVUqDnYvydbe1hTEZfoXyN3QHm6QZLYtKB0RqR4HlcufnFWkCAwEA
|
||||
ATANBgkqhkiG9w0BAQsFAAOCAQEAtgG/2/7q2xPJG+Z0xJIyS9O7h6Igus2LhrAw
|
||||
tkudX5FZtJclBmDx3KvtJMPzpoGFRF48nXaqNcDSI3+8MiMk+JfpfGtmFbYuaa+l
|
||||
seqk/3byv/y1ofd8JMk0olY219tI1/BK4AU+fPP1obYV0tFcwIDOqQaS6f/N4RP1
|
||||
MO0PE+j9dDO6GQjIEUHZVVis70WvGaTc6DfGPVehZzDbNW9nTdkuacZf+XtlLVXq
|
||||
LzJpGeMxvX7SRRkdyegNigiWHJzOchVcoNnO+rUm9JDjY8pukDC08uZEQVqNpOXD
|
||||
tO1t4rpBT9HWxPRCLt8IwOhoYAGb5DWwUeIaiY637CSPTJVjCQ==
|
||||
-----END CERTIFICATE-----
|
||||
27
test/tlssamesubdn/samesubdn.key
Normal file
27
test/tlssamesubdn/samesubdn.key
Normal file
@@ -0,0 +1,27 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEogIBAAKCAQEAsk0AaKsLIvOwelXZzKyjylzYE4/iPqW7ZlOx1IT5ZVmypfdh
|
||||
aZXxMJiklBEPiOaWtggR1gAaYo9m9u9aOHNlY4kjF8gwZHBDh2XxtINmlxzs4Bwe
|
||||
AtpbU6czLb/U2BXLJc4pNobg+bNkLNIqeEMXz/4WFSashiLkRTpufUy5jfgc6zAB
|
||||
q7QbthV6ugFQ+z4tSNSlDHi+nkFrP2xYHNZy+xmyhKgtMxYSoKhypg6GPUAuvA3r
|
||||
U3N1HBW2hLjV1yMcCIUoRVMohuoTSJvns5/WyiE712smCN6AE2LXyWxVSoOdi/J1
|
||||
t7WFMRl+hfI3dAebpBkti0oHRGpHgeVy5+cVaQIDAQABAoIBAGlAmVAwQBe24OOm
|
||||
kDaJZvrLSeZqVnUC4pgqKdy5Tnusso/Uc5WfpMw6H1UkqRX4gNkd9GFumCS8YFy+
|
||||
uHSAckaKqsOcMizoNITWAhO8SbBEq/QzxOeMPMMp/UUxa5TPcKd1htCgWGgupKDs
|
||||
w5NQj9sBM8nylX9tU3EmaBjhVNvfsZSeA76s0z+JDDwJLUlH33dwvgidw+baRxYM
|
||||
U6Tr3Z7YCFZ7+iZbOTHZjkKVtS2EqHtU5OW/pQltbBSovhYQMXjBsCHpTK78+jbV
|
||||
jKjtDUYWplcAYoN/O8ljYHU6lKQlTfo8KQInLtek/Ycen7uOCT6kFAreWFg08g4x
|
||||
5IH/pXECgYEA4ivLQjKR1BHiO+6rDZIfQxvbTa2RgT3Yr9W923OpGY1LiuapGFfJ
|
||||
97PjVKQf6HromQuzveYZR9YFKR4u8YTUn5PCB16v9MEc7nxnwIfE1kH5aEMvMSq9
|
||||
MvxvYObs3dJu4dx+AF8q8REuviBkQ2br34ukbl8ssaB/7zHPXyNJDt0CgYEAydD3
|
||||
BNtPRwKorQKOC1VXQxAFtlu/iiia4LEocAODDUX8N0iGxRBpQcfY/OuqV2j8Alfx
|
||||
n7+Zv/NGEah+WLaApc6wZVvOOuJevam/3ZJReawUtpCcG5em388WSQDuIj3AKhVV
|
||||
IzRRRFlGrTuHENdsqG5rBzbYWvans4lpVkB6Kf0CgYA0YUQfvqp7XPDyRGIlMHRB
|
||||
DJCLuuj576LhhjUEQBMnscuPDcCXvK0vyt+ZWLFzHBQGbelgz4uHUY+8aBsjIEpF
|
||||
8uh64HkLzDWxHerBcjAqFvl2Jiklz+olhsUcwh6VeQjpEjG0UFYXoo0ax1GxMoLq
|
||||
MOMSFMS7FprKgNSwCfH/mQKBgBLA8lNnNcS5gIcjN6Ph+EvpDz7U48Wo5EuA6usN
|
||||
yH3RRRC2Ep/5WG6ebZGDLL8WqGRnW7KmkVj++EHn0GXZ/7ZosIeasl1Sb89cGNI0
|
||||
KJIP5ZTZd0gxHXaqvb1m8roH1vWSKektkWeyjBjI8VIlUpmMOTHgbNZ3GTpbyIgV
|
||||
UcTZAoGAJp5AX+5u6OhLcCI56dpKYGiIS+8fXd2+oz7uZYf2yNTZONulehh2earM
|
||||
4soiUF5ArPgc3KbZhorvIXjOUSb5wc7I2nQKxXQt9j2fyZMD8qsyI1kBqLQ53Z0r
|
||||
agAkX1xMvhvHMQFKmPMXt3pUfXEx/oa32f3ciyPAhyyQNpv5gx4=
|
||||
-----END RSA PRIVATE KEY-----
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/bash
|
||||
# -*- mode: sh -*-
|
||||
# © Copyright IBM Corporation 2020
|
||||
# © Copyright IBM Corporation 2020, 2023
|
||||
#
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@@ -35,10 +35,12 @@ go_minor="${go_version_parts[1]}"
|
||||
if [[ "$go_major" -eq 1 && "$go_minor" -lt 18 ]]; then
|
||||
echo "Go version ${go_major}.${go_minor} < 1.18... Pinning credential-helper commit"
|
||||
git checkout ab7fd12c67d83193072fa91e5648b036547f6323
|
||||
make pass
|
||||
cp bin/docker-credential-pass $GOPATH/bin/docker-credential-pass
|
||||
else
|
||||
make pass
|
||||
cp bin/build/docker-credential-pass $GOPATH/bin/docker-credential-pass
|
||||
fi
|
||||
|
||||
make pass
|
||||
cp bin/docker-credential-pass $GOPATH/bin/docker-credential-pass
|
||||
mkdir -p /home/travis/.docker
|
||||
echo '{ "credsStore": "pass" }' | tee /home/travis/.docker/config.json
|
||||
gpg2 --batch --gen-key <<-EOF
|
||||
|
||||
3
vendor/github.com/cespare/xxhash/v2/go.mod
generated
vendored
3
vendor/github.com/cespare/xxhash/v2/go.mod
generated
vendored
@@ -1,3 +0,0 @@
|
||||
module github.com/cespare/xxhash/v2
|
||||
|
||||
go 1.11
|
||||
0
vendor/github.com/cespare/xxhash/v2/go.sum
generated
vendored
0
vendor/github.com/cespare/xxhash/v2/go.sum
generated
vendored
2
vendor/github.com/golang/protobuf/proto/text_decode.go
generated
vendored
2
vendor/github.com/golang/protobuf/proto/text_decode.go
generated
vendored
@@ -765,7 +765,7 @@ func unescape(s string) (ch string, tail string, err error) {
|
||||
if i > utf8.MaxRune {
|
||||
return "", "", fmt.Errorf(`\%c%s is not a valid Unicode code point`, r, ss)
|
||||
}
|
||||
return string(i), s, nil
|
||||
return string(rune(i)), s, nil
|
||||
}
|
||||
return "", "", fmt.Errorf(`unknown escape \%c`, r)
|
||||
}
|
||||
|
||||
20
vendor/github.com/prometheus/client_golang/prometheus/counter.go
generated
vendored
20
vendor/github.com/prometheus/client_golang/prometheus/counter.go
generated
vendored
@@ -163,7 +163,7 @@ func (c *counter) updateExemplar(v float64, l Labels) {
|
||||
// (e.g. number of HTTP requests, partitioned by response code and
|
||||
// method). Create instances with NewCounterVec.
|
||||
type CounterVec struct {
|
||||
*metricVec
|
||||
*MetricVec
|
||||
}
|
||||
|
||||
// NewCounterVec creates a new CounterVec based on the provided CounterOpts and
|
||||
@@ -176,11 +176,11 @@ func NewCounterVec(opts CounterOpts, labelNames []string) *CounterVec {
|
||||
opts.ConstLabels,
|
||||
)
|
||||
return &CounterVec{
|
||||
metricVec: newMetricVec(desc, func(lvs ...string) Metric {
|
||||
MetricVec: NewMetricVec(desc, func(lvs ...string) Metric {
|
||||
if len(lvs) != len(desc.variableLabels) {
|
||||
panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels, lvs))
|
||||
}
|
||||
result := &counter{desc: desc, labelPairs: makeLabelPairs(desc, lvs), now: time.Now}
|
||||
result := &counter{desc: desc, labelPairs: MakeLabelPairs(desc, lvs), now: time.Now}
|
||||
result.init(result) // Init self-collection.
|
||||
return result
|
||||
}),
|
||||
@@ -188,7 +188,7 @@ func NewCounterVec(opts CounterOpts, labelNames []string) *CounterVec {
|
||||
}
|
||||
|
||||
// GetMetricWithLabelValues returns the Counter for the given slice of label
|
||||
// values (same order as the VariableLabels in Desc). If that combination of
|
||||
// values (same order as the variable labels in Desc). If that combination of
|
||||
// label values is accessed for the first time, a new Counter is created.
|
||||
//
|
||||
// It is possible to call this method without using the returned Counter to only
|
||||
@@ -202,7 +202,7 @@ func NewCounterVec(opts CounterOpts, labelNames []string) *CounterVec {
|
||||
// Counter with the same label values is created later.
|
||||
//
|
||||
// An error is returned if the number of label values is not the same as the
|
||||
// number of VariableLabels in Desc (minus any curried labels).
|
||||
// number of variable labels in Desc (minus any curried labels).
|
||||
//
|
||||
// Note that for more than one label value, this method is prone to mistakes
|
||||
// caused by an incorrect order of arguments. Consider GetMetricWith(Labels) as
|
||||
@@ -211,7 +211,7 @@ func NewCounterVec(opts CounterOpts, labelNames []string) *CounterVec {
|
||||
// with a performance overhead (for creating and processing the Labels map).
|
||||
// See also the GaugeVec example.
|
||||
func (v *CounterVec) GetMetricWithLabelValues(lvs ...string) (Counter, error) {
|
||||
metric, err := v.metricVec.getMetricWithLabelValues(lvs...)
|
||||
metric, err := v.MetricVec.GetMetricWithLabelValues(lvs...)
|
||||
if metric != nil {
|
||||
return metric.(Counter), err
|
||||
}
|
||||
@@ -219,19 +219,19 @@ func (v *CounterVec) GetMetricWithLabelValues(lvs ...string) (Counter, error) {
|
||||
}
|
||||
|
||||
// GetMetricWith returns the Counter for the given Labels map (the label names
|
||||
// must match those of the VariableLabels in Desc). If that label map is
|
||||
// must match those of the variable labels in Desc). If that label map is
|
||||
// accessed for the first time, a new Counter is created. Implications of
|
||||
// creating a Counter without using it and keeping the Counter for later use are
|
||||
// the same as for GetMetricWithLabelValues.
|
||||
//
|
||||
// An error is returned if the number and names of the Labels are inconsistent
|
||||
// with those of the VariableLabels in Desc (minus any curried labels).
|
||||
// with those of the variable labels in Desc (minus any curried labels).
|
||||
//
|
||||
// This method is used for the same purpose as
|
||||
// GetMetricWithLabelValues(...string). See there for pros and cons of the two
|
||||
// methods.
|
||||
func (v *CounterVec) GetMetricWith(labels Labels) (Counter, error) {
|
||||
metric, err := v.metricVec.getMetricWith(labels)
|
||||
metric, err := v.MetricVec.GetMetricWith(labels)
|
||||
if metric != nil {
|
||||
return metric.(Counter), err
|
||||
}
|
||||
@@ -275,7 +275,7 @@ func (v *CounterVec) With(labels Labels) Counter {
|
||||
// registered with a given registry (usually the uncurried version). The Reset
|
||||
// method deletes all metrics, even if called on a curried vector.
|
||||
func (v *CounterVec) CurryWith(labels Labels) (*CounterVec, error) {
|
||||
vec, err := v.curryWith(labels)
|
||||
vec, err := v.MetricVec.CurryWith(labels)
|
||||
if vec != nil {
|
||||
return &CounterVec{vec}, err
|
||||
}
|
||||
|
||||
4
vendor/github.com/prometheus/client_golang/prometheus/desc.go
generated
vendored
4
vendor/github.com/prometheus/client_golang/prometheus/desc.go
generated
vendored
@@ -20,7 +20,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/cespare/xxhash/v2"
|
||||
//lint:ignore SA1019 Need to keep deprecated package for compatibility.
|
||||
//nolint:staticcheck // Ignore SA1019. Need to keep deprecated package for compatibility.
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/prometheus/common/model"
|
||||
|
||||
@@ -51,7 +51,7 @@ type Desc struct {
|
||||
// constLabelPairs contains precalculated DTO label pairs based on
|
||||
// the constant labels.
|
||||
constLabelPairs []*dto.LabelPair
|
||||
// VariableLabels contains names of labels for which the metric
|
||||
// variableLabels contains names of labels for which the metric
|
||||
// maintains variable values.
|
||||
variableLabels []string
|
||||
// id is a hash of the values of the ConstLabels and fqName. This
|
||||
|
||||
39
vendor/github.com/prometheus/client_golang/prometheus/expvar_collector.go
generated
vendored
39
vendor/github.com/prometheus/client_golang/prometheus/expvar_collector.go
generated
vendored
@@ -22,43 +22,10 @@ type expvarCollector struct {
|
||||
exports map[string]*Desc
|
||||
}
|
||||
|
||||
// NewExpvarCollector returns a newly allocated expvar Collector that still has
|
||||
// to be registered with a Prometheus registry.
|
||||
// NewExpvarCollector is the obsolete version of collectors.NewExpvarCollector.
|
||||
// See there for documentation.
|
||||
//
|
||||
// An expvar Collector collects metrics from the expvar interface. It provides a
|
||||
// quick way to expose numeric values that are already exported via expvar as
|
||||
// Prometheus metrics. Note that the data models of expvar and Prometheus are
|
||||
// fundamentally different, and that the expvar Collector is inherently slower
|
||||
// than native Prometheus metrics. Thus, the expvar Collector is probably great
|
||||
// for experiments and prototying, but you should seriously consider a more
|
||||
// direct implementation of Prometheus metrics for monitoring production
|
||||
// systems.
|
||||
//
|
||||
// The exports map has the following meaning:
|
||||
//
|
||||
// The keys in the map correspond to expvar keys, i.e. for every expvar key you
|
||||
// want to export as Prometheus metric, you need an entry in the exports
|
||||
// map. The descriptor mapped to each key describes how to export the expvar
|
||||
// value. It defines the name and the help string of the Prometheus metric
|
||||
// proxying the expvar value. The type will always be Untyped.
|
||||
//
|
||||
// For descriptors without variable labels, the expvar value must be a number or
|
||||
// a bool. The number is then directly exported as the Prometheus sample
|
||||
// value. (For a bool, 'false' translates to 0 and 'true' to 1). Expvar values
|
||||
// that are not numbers or bools are silently ignored.
|
||||
//
|
||||
// If the descriptor has one variable label, the expvar value must be an expvar
|
||||
// map. The keys in the expvar map become the various values of the one
|
||||
// Prometheus label. The values in the expvar map must be numbers or bools again
|
||||
// as above.
|
||||
//
|
||||
// For descriptors with more than one variable label, the expvar must be a
|
||||
// nested expvar map, i.e. where the values of the topmost map are maps again
|
||||
// etc. until a depth is reached that corresponds to the number of labels. The
|
||||
// leaves of that structure must be numbers or bools as above to serve as the
|
||||
// sample values.
|
||||
//
|
||||
// Anything that does not fit into the scheme above is silently ignored.
|
||||
// Deprecated: Use collectors.NewExpvarCollector instead.
|
||||
func NewExpvarCollector(exports map[string]*Desc) Collector {
|
||||
return &expvarCollector{
|
||||
exports: exports,
|
||||
|
||||
20
vendor/github.com/prometheus/client_golang/prometheus/gauge.go
generated
vendored
20
vendor/github.com/prometheus/client_golang/prometheus/gauge.go
generated
vendored
@@ -132,7 +132,7 @@ func (g *gauge) Write(out *dto.Metric) error {
|
||||
// (e.g. number of operations queued, partitioned by user and operation
|
||||
// type). Create instances with NewGaugeVec.
|
||||
type GaugeVec struct {
|
||||
*metricVec
|
||||
*MetricVec
|
||||
}
|
||||
|
||||
// NewGaugeVec creates a new GaugeVec based on the provided GaugeOpts and
|
||||
@@ -145,11 +145,11 @@ func NewGaugeVec(opts GaugeOpts, labelNames []string) *GaugeVec {
|
||||
opts.ConstLabels,
|
||||
)
|
||||
return &GaugeVec{
|
||||
metricVec: newMetricVec(desc, func(lvs ...string) Metric {
|
||||
MetricVec: NewMetricVec(desc, func(lvs ...string) Metric {
|
||||
if len(lvs) != len(desc.variableLabels) {
|
||||
panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels, lvs))
|
||||
}
|
||||
result := &gauge{desc: desc, labelPairs: makeLabelPairs(desc, lvs)}
|
||||
result := &gauge{desc: desc, labelPairs: MakeLabelPairs(desc, lvs)}
|
||||
result.init(result) // Init self-collection.
|
||||
return result
|
||||
}),
|
||||
@@ -157,7 +157,7 @@ func NewGaugeVec(opts GaugeOpts, labelNames []string) *GaugeVec {
|
||||
}
|
||||
|
||||
// GetMetricWithLabelValues returns the Gauge for the given slice of label
|
||||
// values (same order as the VariableLabels in Desc). If that combination of
|
||||
// values (same order as the variable labels in Desc). If that combination of
|
||||
// label values is accessed for the first time, a new Gauge is created.
|
||||
//
|
||||
// It is possible to call this method without using the returned Gauge to only
|
||||
@@ -172,7 +172,7 @@ func NewGaugeVec(opts GaugeOpts, labelNames []string) *GaugeVec {
|
||||
// example.
|
||||
//
|
||||
// An error is returned if the number of label values is not the same as the
|
||||
// number of VariableLabels in Desc (minus any curried labels).
|
||||
// number of variable labels in Desc (minus any curried labels).
|
||||
//
|
||||
// Note that for more than one label value, this method is prone to mistakes
|
||||
// caused by an incorrect order of arguments. Consider GetMetricWith(Labels) as
|
||||
@@ -180,7 +180,7 @@ func NewGaugeVec(opts GaugeOpts, labelNames []string) *GaugeVec {
|
||||
// latter has a much more readable (albeit more verbose) syntax, but it comes
|
||||
// with a performance overhead (for creating and processing the Labels map).
|
||||
func (v *GaugeVec) GetMetricWithLabelValues(lvs ...string) (Gauge, error) {
|
||||
metric, err := v.metricVec.getMetricWithLabelValues(lvs...)
|
||||
metric, err := v.MetricVec.GetMetricWithLabelValues(lvs...)
|
||||
if metric != nil {
|
||||
return metric.(Gauge), err
|
||||
}
|
||||
@@ -188,19 +188,19 @@ func (v *GaugeVec) GetMetricWithLabelValues(lvs ...string) (Gauge, error) {
|
||||
}
|
||||
|
||||
// GetMetricWith returns the Gauge for the given Labels map (the label names
|
||||
// must match those of the VariableLabels in Desc). If that label map is
|
||||
// must match those of the variable labels in Desc). If that label map is
|
||||
// accessed for the first time, a new Gauge is created. Implications of
|
||||
// creating a Gauge without using it and keeping the Gauge for later use are
|
||||
// the same as for GetMetricWithLabelValues.
|
||||
//
|
||||
// An error is returned if the number and names of the Labels are inconsistent
|
||||
// with those of the VariableLabels in Desc (minus any curried labels).
|
||||
// with those of the variable labels in Desc (minus any curried labels).
|
||||
//
|
||||
// This method is used for the same purpose as
|
||||
// GetMetricWithLabelValues(...string). See there for pros and cons of the two
|
||||
// methods.
|
||||
func (v *GaugeVec) GetMetricWith(labels Labels) (Gauge, error) {
|
||||
metric, err := v.metricVec.getMetricWith(labels)
|
||||
metric, err := v.MetricVec.GetMetricWith(labels)
|
||||
if metric != nil {
|
||||
return metric.(Gauge), err
|
||||
}
|
||||
@@ -244,7 +244,7 @@ func (v *GaugeVec) With(labels Labels) Gauge {
|
||||
// registered with a given registry (usually the uncurried version). The Reset
|
||||
// method deletes all metrics, even if called on a curried vector.
|
||||
func (v *GaugeVec) CurryWith(labels Labels) (*GaugeVec, error) {
|
||||
vec, err := v.curryWith(labels)
|
||||
vec, err := v.MetricVec.CurryWith(labels)
|
||||
if vec != nil {
|
||||
return &GaugeVec{vec}, err
|
||||
}
|
||||
|
||||
53
vendor/github.com/prometheus/client_golang/prometheus/go_collector.go
generated
vendored
53
vendor/github.com/prometheus/client_golang/prometheus/go_collector.go
generated
vendored
@@ -36,31 +36,10 @@ type goCollector struct {
|
||||
msMaxAge time.Duration // Maximum allowed age of old memstats.
|
||||
}
|
||||
|
||||
// NewGoCollector returns a collector that exports metrics about the current Go
|
||||
// process. This includes memory stats. To collect those, runtime.ReadMemStats
|
||||
// is called. This requires to “stop the world”, which usually only happens for
|
||||
// garbage collection (GC). Take the following implications into account when
|
||||
// deciding whether to use the Go collector:
|
||||
// NewGoCollector is the obsolete version of collectors.NewGoCollector.
|
||||
// See there for documentation.
|
||||
//
|
||||
// 1. The performance impact of stopping the world is the more relevant the more
|
||||
// frequently metrics are collected. However, with Go1.9 or later the
|
||||
// stop-the-world time per metrics collection is very short (~25µs) so that the
|
||||
// performance impact will only matter in rare cases. However, with older Go
|
||||
// versions, the stop-the-world duration depends on the heap size and can be
|
||||
// quite significant (~1.7 ms/GiB as per
|
||||
// https://go-review.googlesource.com/c/go/+/34937).
|
||||
//
|
||||
// 2. During an ongoing GC, nothing else can stop the world. Therefore, if the
|
||||
// metrics collection happens to coincide with GC, it will only complete after
|
||||
// GC has finished. Usually, GC is fast enough to not cause problems. However,
|
||||
// with a very large heap, GC might take multiple seconds, which is enough to
|
||||
// cause scrape timeouts in common setups. To avoid this problem, the Go
|
||||
// collector will use the memstats from a previous collection if
|
||||
// runtime.ReadMemStats takes more than 1s. However, if there are no previously
|
||||
// collected memstats, or their collection is more than 5m ago, the collection
|
||||
// will block until runtime.ReadMemStats succeeds. (The problem might be solved
|
||||
// in Go1.13, see https://github.com/golang/go/issues/19812 for the related Go
|
||||
// issue.)
|
||||
// Deprecated: Use collectors.NewGoCollector instead.
|
||||
func NewGoCollector() Collector {
|
||||
return &goCollector{
|
||||
goroutinesDesc: NewDesc(
|
||||
@@ -365,25 +344,17 @@ type memStatsMetrics []struct {
|
||||
valType ValueType
|
||||
}
|
||||
|
||||
// NewBuildInfoCollector returns a collector collecting a single metric
|
||||
// "go_build_info" with the constant value 1 and three labels "path", "version",
|
||||
// and "checksum". Their label values contain the main module path, version, and
|
||||
// checksum, respectively. The labels will only have meaningful values if the
|
||||
// binary is built with Go module support and from source code retrieved from
|
||||
// the source repository (rather than the local file system). This is usually
|
||||
// accomplished by building from outside of GOPATH, specifying the full address
|
||||
// of the main package, e.g. "GO111MODULE=on go run
|
||||
// github.com/prometheus/client_golang/examples/random". If built without Go
|
||||
// module support, all label values will be "unknown". If built with Go module
|
||||
// support but using the source code from the local file system, the "path" will
|
||||
// be set appropriately, but "checksum" will be empty and "version" will be
|
||||
// "(devel)".
|
||||
// NewBuildInfoCollector is the obsolete version of collectors.NewBuildInfoCollector.
|
||||
// See there for documentation.
|
||||
//
|
||||
// This collector uses only the build information for the main module. See
|
||||
// https://github.com/povilasv/prommod for an example of a collector for the
|
||||
// module dependencies.
|
||||
// Deprecated: Use collectors.NewBuildInfoCollector instead.
|
||||
func NewBuildInfoCollector() Collector {
|
||||
path, version, sum := readBuildInfo()
|
||||
path, version, sum := "unknown", "unknown", "unknown"
|
||||
if bi, ok := debug.ReadBuildInfo(); ok {
|
||||
path = bi.Main.Path
|
||||
version = bi.Main.Version
|
||||
sum = bi.Main.Sum
|
||||
}
|
||||
c := &selfCollector{MustNewConstMetric(
|
||||
NewDesc(
|
||||
"go_build_info",
|
||||
|
||||
31
vendor/github.com/prometheus/client_golang/prometheus/histogram.go
generated
vendored
31
vendor/github.com/prometheus/client_golang/prometheus/histogram.go
generated
vendored
@@ -22,7 +22,7 @@ import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
//lint:ignore SA1019 Need to keep deprecated package for compatibility.
|
||||
//nolint:staticcheck // Ignore SA1019. Need to keep deprecated package for compatibility.
|
||||
"github.com/golang/protobuf/proto"
|
||||
|
||||
dto "github.com/prometheus/client_model/go"
|
||||
@@ -47,7 +47,12 @@ type Histogram interface {
|
||||
Metric
|
||||
Collector
|
||||
|
||||
// Observe adds a single observation to the histogram.
|
||||
// Observe adds a single observation to the histogram. Observations are
|
||||
// usually positive or zero. Negative observations are accepted but
|
||||
// prevent current versions of Prometheus from properly detecting
|
||||
// counter resets in the sum of observations. See
|
||||
// https://prometheus.io/docs/practices/histograms/#count-and-sum-of-observations
|
||||
// for details.
|
||||
Observe(float64)
|
||||
}
|
||||
|
||||
@@ -192,7 +197,7 @@ func newHistogram(desc *Desc, opts HistogramOpts, labelValues ...string) Histogr
|
||||
h := &histogram{
|
||||
desc: desc,
|
||||
upperBounds: opts.Buckets,
|
||||
labelPairs: makeLabelPairs(desc, labelValues),
|
||||
labelPairs: MakeLabelPairs(desc, labelValues),
|
||||
counts: [2]*histogramCounts{{}, {}},
|
||||
now: time.Now,
|
||||
}
|
||||
@@ -409,7 +414,7 @@ func (h *histogram) updateExemplar(v float64, bucket int, l Labels) {
|
||||
// (e.g. HTTP request latencies, partitioned by status code and method). Create
|
||||
// instances with NewHistogramVec.
|
||||
type HistogramVec struct {
|
||||
*metricVec
|
||||
*MetricVec
|
||||
}
|
||||
|
||||
// NewHistogramVec creates a new HistogramVec based on the provided HistogramOpts and
|
||||
@@ -422,14 +427,14 @@ func NewHistogramVec(opts HistogramOpts, labelNames []string) *HistogramVec {
|
||||
opts.ConstLabels,
|
||||
)
|
||||
return &HistogramVec{
|
||||
metricVec: newMetricVec(desc, func(lvs ...string) Metric {
|
||||
MetricVec: NewMetricVec(desc, func(lvs ...string) Metric {
|
||||
return newHistogram(desc, opts, lvs...)
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
// GetMetricWithLabelValues returns the Histogram for the given slice of label
|
||||
// values (same order as the VariableLabels in Desc). If that combination of
|
||||
// values (same order as the variable labels in Desc). If that combination of
|
||||
// label values is accessed for the first time, a new Histogram is created.
|
||||
//
|
||||
// It is possible to call this method without using the returned Histogram to only
|
||||
@@ -444,7 +449,7 @@ func NewHistogramVec(opts HistogramOpts, labelNames []string) *HistogramVec {
|
||||
// example.
|
||||
//
|
||||
// An error is returned if the number of label values is not the same as the
|
||||
// number of VariableLabels in Desc (minus any curried labels).
|
||||
// number of variable labels in Desc (minus any curried labels).
|
||||
//
|
||||
// Note that for more than one label value, this method is prone to mistakes
|
||||
// caused by an incorrect order of arguments. Consider GetMetricWith(Labels) as
|
||||
@@ -453,7 +458,7 @@ func NewHistogramVec(opts HistogramOpts, labelNames []string) *HistogramVec {
|
||||
// with a performance overhead (for creating and processing the Labels map).
|
||||
// See also the GaugeVec example.
|
||||
func (v *HistogramVec) GetMetricWithLabelValues(lvs ...string) (Observer, error) {
|
||||
metric, err := v.metricVec.getMetricWithLabelValues(lvs...)
|
||||
metric, err := v.MetricVec.GetMetricWithLabelValues(lvs...)
|
||||
if metric != nil {
|
||||
return metric.(Observer), err
|
||||
}
|
||||
@@ -461,19 +466,19 @@ func (v *HistogramVec) GetMetricWithLabelValues(lvs ...string) (Observer, error)
|
||||
}
|
||||
|
||||
// GetMetricWith returns the Histogram for the given Labels map (the label names
|
||||
// must match those of the VariableLabels in Desc). If that label map is
|
||||
// must match those of the variable labels in Desc). If that label map is
|
||||
// accessed for the first time, a new Histogram is created. Implications of
|
||||
// creating a Histogram without using it and keeping the Histogram for later use
|
||||
// are the same as for GetMetricWithLabelValues.
|
||||
//
|
||||
// An error is returned if the number and names of the Labels are inconsistent
|
||||
// with those of the VariableLabels in Desc (minus any curried labels).
|
||||
// with those of the variable labels in Desc (minus any curried labels).
|
||||
//
|
||||
// This method is used for the same purpose as
|
||||
// GetMetricWithLabelValues(...string). See there for pros and cons of the two
|
||||
// methods.
|
||||
func (v *HistogramVec) GetMetricWith(labels Labels) (Observer, error) {
|
||||
metric, err := v.metricVec.getMetricWith(labels)
|
||||
metric, err := v.MetricVec.GetMetricWith(labels)
|
||||
if metric != nil {
|
||||
return metric.(Observer), err
|
||||
}
|
||||
@@ -517,7 +522,7 @@ func (v *HistogramVec) With(labels Labels) Observer {
|
||||
// registered with a given registry (usually the uncurried version). The Reset
|
||||
// method deletes all metrics, even if called on a curried vector.
|
||||
func (v *HistogramVec) CurryWith(labels Labels) (ObserverVec, error) {
|
||||
vec, err := v.curryWith(labels)
|
||||
vec, err := v.MetricVec.CurryWith(labels)
|
||||
if vec != nil {
|
||||
return &HistogramVec{vec}, err
|
||||
}
|
||||
@@ -602,7 +607,7 @@ func NewConstHistogram(
|
||||
count: count,
|
||||
sum: sum,
|
||||
buckets: buckets,
|
||||
labelPairs: makeLabelPairs(desc, labelValues),
|
||||
labelPairs: MakeLabelPairs(desc, labelValues),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
6
vendor/github.com/prometheus/client_golang/prometheus/metric.go
generated
vendored
6
vendor/github.com/prometheus/client_golang/prometheus/metric.go
generated
vendored
@@ -17,7 +17,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
//lint:ignore SA1019 Need to keep deprecated package for compatibility.
|
||||
//nolint:staticcheck // Ignore SA1019. Need to keep deprecated package for compatibility.
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/prometheus/common/model"
|
||||
|
||||
@@ -58,7 +58,7 @@ type Metric interface {
|
||||
}
|
||||
|
||||
// Opts bundles the options for creating most Metric types. Each metric
|
||||
// implementation XXX has its own XXXOpts type, but in most cases, it is just be
|
||||
// implementation XXX has its own XXXOpts type, but in most cases, it is just
|
||||
// an alias of this type (which might change when the requirement arises.)
|
||||
//
|
||||
// It is mandatory to set Name to a non-empty string. All other fields are
|
||||
@@ -89,7 +89,7 @@ type Opts struct {
|
||||
// better covered by target labels set by the scraping Prometheus
|
||||
// server, or by one specific metric (e.g. a build_info or a
|
||||
// machine_role metric). See also
|
||||
// https://prometheus.io/docs/instrumenting/writing_exporters/#target-labels,-not-static-scraped-labels
|
||||
// https://prometheus.io/docs/instrumenting/writing_exporters/#target-labels-not-static-scraped-labels
|
||||
ConstLabels Labels
|
||||
}
|
||||
|
||||
|
||||
33
vendor/github.com/prometheus/client_golang/prometheus/process_collector.go
generated
vendored
33
vendor/github.com/prometheus/client_golang/prometheus/process_collector.go
generated
vendored
@@ -15,7 +15,11 @@ package prometheus
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type processCollector struct {
|
||||
@@ -50,16 +54,10 @@ type ProcessCollectorOpts struct {
|
||||
ReportErrors bool
|
||||
}
|
||||
|
||||
// NewProcessCollector returns a collector which exports the current state of
|
||||
// process metrics including CPU, memory and file descriptor usage as well as
|
||||
// the process start time. The detailed behavior is defined by the provided
|
||||
// ProcessCollectorOpts. The zero value of ProcessCollectorOpts creates a
|
||||
// collector for the current process with an empty namespace string and no error
|
||||
// reporting.
|
||||
// NewProcessCollector is the obsolete version of collectors.NewProcessCollector.
|
||||
// See there for documentation.
|
||||
//
|
||||
// The collector only works on operating systems with a Linux-style proc
|
||||
// filesystem and on Microsoft Windows. On other operating systems, it will not
|
||||
// collect any metrics.
|
||||
// Deprecated: Use collectors.NewProcessCollector instead.
|
||||
func NewProcessCollector(opts ProcessCollectorOpts) Collector {
|
||||
ns := ""
|
||||
if len(opts.Namespace) > 0 {
|
||||
@@ -149,3 +147,20 @@ func (c *processCollector) reportError(ch chan<- Metric, desc *Desc, err error)
|
||||
}
|
||||
ch <- NewInvalidMetric(desc, err)
|
||||
}
|
||||
|
||||
// NewPidFileFn returns a function that retrieves a pid from the specified file.
|
||||
// It is meant to be used for the PidFn field in ProcessCollectorOpts.
|
||||
func NewPidFileFn(pidFilePath string) func() (int, error) {
|
||||
return func() (int, error) {
|
||||
content, err := ioutil.ReadFile(pidFilePath)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("can't read pid file %q: %+v", pidFilePath, err)
|
||||
}
|
||||
pid, err := strconv.Atoi(strings.TrimSpace(string(content)))
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("can't parse pid file %q: %+v", pidFilePath, err)
|
||||
}
|
||||
|
||||
return pid, nil
|
||||
}
|
||||
}
|
||||
|
||||
6
vendor/github.com/prometheus/client_golang/prometheus/promhttp/delegator.go
generated
vendored
6
vendor/github.com/prometheus/client_golang/prometheus/promhttp/delegator.go
generated
vendored
@@ -83,8 +83,7 @@ type readerFromDelegator struct{ *responseWriterDelegator }
|
||||
type pusherDelegator struct{ *responseWriterDelegator }
|
||||
|
||||
func (d closeNotifierDelegator) CloseNotify() <-chan bool {
|
||||
//lint:ignore SA1019 http.CloseNotifier is deprecated but we don't want to
|
||||
//remove support from client_golang yet.
|
||||
//nolint:staticcheck // Ignore SA1019. http.CloseNotifier is deprecated but we keep it here to not break existing users.
|
||||
return d.ResponseWriter.(http.CloseNotifier).CloseNotify()
|
||||
}
|
||||
func (d flusherDelegator) Flush() {
|
||||
@@ -348,8 +347,7 @@ func newDelegator(w http.ResponseWriter, observeWriteHeaderFunc func(int)) deleg
|
||||
}
|
||||
|
||||
id := 0
|
||||
//lint:ignore SA1019 http.CloseNotifier is deprecated but we don't want to
|
||||
//remove support from client_golang yet.
|
||||
//nolint:staticcheck // Ignore SA1019. http.CloseNotifier is deprecated but we keep it here to not break existing users.
|
||||
if _, ok := w.(http.CloseNotifier); ok {
|
||||
id += closeNotifier
|
||||
}
|
||||
|
||||
10
vendor/github.com/prometheus/client_golang/prometheus/promhttp/http.go
generated
vendored
10
vendor/github.com/prometheus/client_golang/prometheus/promhttp/http.go
generated
vendored
@@ -99,7 +99,7 @@ func HandlerFor(reg prometheus.Gatherer, opts HandlerOpts) http.Handler {
|
||||
inFlightSem = make(chan struct{}, opts.MaxRequestsInFlight)
|
||||
}
|
||||
if opts.Registry != nil {
|
||||
// Initialize all possibilites that can occur below.
|
||||
// Initialize all possibilities that can occur below.
|
||||
errCnt.WithLabelValues("gathering")
|
||||
errCnt.WithLabelValues("encoding")
|
||||
if err := opts.Registry.Register(errCnt); err != nil {
|
||||
@@ -303,8 +303,12 @@ type Logger interface {
|
||||
// HandlerOpts specifies options how to serve metrics via an http.Handler. The
|
||||
// zero value of HandlerOpts is a reasonable default.
|
||||
type HandlerOpts struct {
|
||||
// ErrorLog specifies an optional logger for errors collecting and
|
||||
// serving metrics. If nil, errors are not logged at all.
|
||||
// ErrorLog specifies an optional Logger for errors collecting and
|
||||
// serving metrics. If nil, errors are not logged at all. Note that the
|
||||
// type of a reported error is often prometheus.MultiError, which
|
||||
// formats into a multi-line error string. If you want to avoid the
|
||||
// latter, create a Logger implementation that detects a
|
||||
// prometheus.MultiError and formats the contained errors into one line.
|
||||
ErrorLog Logger
|
||||
// ErrorHandling defines how errors are handled. Note that errors are
|
||||
// logged regardless of the configured ErrorHandling provided ErrorLog
|
||||
|
||||
28
vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_client.go
generated
vendored
28
vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_client.go
generated
vendored
@@ -49,7 +49,10 @@ func InstrumentRoundTripperInFlight(gauge prometheus.Gauge, next http.RoundTripp
|
||||
// http.RoundTripper to observe the request result with the provided CounterVec.
|
||||
// The CounterVec must have zero, one, or two non-const non-curried labels. For
|
||||
// those, the only allowed label names are "code" and "method". The function
|
||||
// panics otherwise. Partitioning of the CounterVec happens by HTTP status code
|
||||
// panics otherwise. For the "method" label a predefined default label value set
|
||||
// is used to filter given values. Values besides predefined values will count
|
||||
// as `unknown` method.`WithExtraMethods` can be used to add more
|
||||
// methods to the set. Partitioning of the CounterVec happens by HTTP status code
|
||||
// and/or HTTP method if the respective instance label names are present in the
|
||||
// CounterVec. For unpartitioned counting, use a CounterVec with zero labels.
|
||||
//
|
||||
@@ -57,13 +60,18 @@ func InstrumentRoundTripperInFlight(gauge prometheus.Gauge, next http.RoundTripp
|
||||
// is not incremented.
|
||||
//
|
||||
// See the example for ExampleInstrumentRoundTripperDuration for example usage.
|
||||
func InstrumentRoundTripperCounter(counter *prometheus.CounterVec, next http.RoundTripper) RoundTripperFunc {
|
||||
func InstrumentRoundTripperCounter(counter *prometheus.CounterVec, next http.RoundTripper, opts ...Option) RoundTripperFunc {
|
||||
rtOpts := &option{}
|
||||
for _, o := range opts {
|
||||
o(rtOpts)
|
||||
}
|
||||
|
||||
code, method := checkLabels(counter)
|
||||
|
||||
return RoundTripperFunc(func(r *http.Request) (*http.Response, error) {
|
||||
resp, err := next.RoundTrip(r)
|
||||
if err == nil {
|
||||
counter.With(labels(code, method, r.Method, resp.StatusCode)).Inc()
|
||||
counter.With(labels(code, method, r.Method, resp.StatusCode, rtOpts.extraMethods...)).Inc()
|
||||
}
|
||||
return resp, err
|
||||
})
|
||||
@@ -73,7 +81,10 @@ func InstrumentRoundTripperCounter(counter *prometheus.CounterVec, next http.Rou
|
||||
// http.RoundTripper to observe the request duration with the provided
|
||||
// ObserverVec. The ObserverVec must have zero, one, or two non-const
|
||||
// non-curried labels. For those, the only allowed label names are "code" and
|
||||
// "method". The function panics otherwise. The Observe method of the Observer
|
||||
// "method". The function panics otherwise. For the "method" label a predefined
|
||||
// default label value set is used to filter given values. Values besides
|
||||
// predefined values will count as `unknown` method. `WithExtraMethods`
|
||||
// can be used to add more methods to the set. The Observe method of the Observer
|
||||
// in the ObserverVec is called with the request duration in
|
||||
// seconds. Partitioning happens by HTTP status code and/or HTTP method if the
|
||||
// respective instance label names are present in the ObserverVec. For
|
||||
@@ -85,14 +96,19 @@ func InstrumentRoundTripperCounter(counter *prometheus.CounterVec, next http.Rou
|
||||
//
|
||||
// Note that this method is only guaranteed to never observe negative durations
|
||||
// if used with Go1.9+.
|
||||
func InstrumentRoundTripperDuration(obs prometheus.ObserverVec, next http.RoundTripper) RoundTripperFunc {
|
||||
func InstrumentRoundTripperDuration(obs prometheus.ObserverVec, next http.RoundTripper, opts ...Option) RoundTripperFunc {
|
||||
rtOpts := &option{}
|
||||
for _, o := range opts {
|
||||
o(rtOpts)
|
||||
}
|
||||
|
||||
code, method := checkLabels(obs)
|
||||
|
||||
return RoundTripperFunc(func(r *http.Request) (*http.Response, error) {
|
||||
start := time.Now()
|
||||
resp, err := next.RoundTrip(r)
|
||||
if err == nil {
|
||||
obs.With(labels(code, method, r.Method, resp.StatusCode)).Observe(time.Since(start).Seconds())
|
||||
obs.With(labels(code, method, r.Method, resp.StatusCode, rtOpts.extraMethods...)).Observe(time.Since(start).Seconds())
|
||||
}
|
||||
return resp, err
|
||||
})
|
||||
|
||||
188
vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_server.go
generated
vendored
188
vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_server.go
generated
vendored
@@ -43,14 +43,17 @@ func InstrumentHandlerInFlight(g prometheus.Gauge, next http.Handler) http.Handl
|
||||
|
||||
// InstrumentHandlerDuration is a middleware that wraps the provided
|
||||
// http.Handler to observe the request duration with the provided ObserverVec.
|
||||
// The ObserverVec must have zero, one, or two non-const non-curried labels. For
|
||||
// those, the only allowed label names are "code" and "method". The function
|
||||
// panics otherwise. The Observe method of the Observer in the ObserverVec is
|
||||
// called with the request duration in seconds. Partitioning happens by HTTP
|
||||
// status code and/or HTTP method if the respective instance label names are
|
||||
// present in the ObserverVec. For unpartitioned observations, use an
|
||||
// ObserverVec with zero labels. Note that partitioning of Histograms is
|
||||
// expensive and should be used judiciously.
|
||||
// The ObserverVec must have valid metric and label names and must have zero,
|
||||
// one, or two non-const non-curried labels. For those, the only allowed label
|
||||
// names are "code" and "method". The function panics otherwise. For the "method"
|
||||
// label a predefined default label value set is used to filter given values.
|
||||
// Values besides predefined values will count as `unknown` method.
|
||||
//`WithExtraMethods` can be used to add more methods to the set. The Observe
|
||||
// method of the Observer in the ObserverVec is called with the request duration
|
||||
// in seconds. Partitioning happens by HTTP status code and/or HTTP method if
|
||||
// the respective instance label names are present in the ObserverVec. For
|
||||
// unpartitioned observations, use an ObserverVec with zero labels. Note that
|
||||
// partitioning of Histograms is expensive and should be used judiciously.
|
||||
//
|
||||
// If the wrapped Handler does not set a status code, a status code of 200 is assumed.
|
||||
//
|
||||
@@ -58,7 +61,12 @@ func InstrumentHandlerInFlight(g prometheus.Gauge, next http.Handler) http.Handl
|
||||
//
|
||||
// Note that this method is only guaranteed to never observe negative durations
|
||||
// if used with Go1.9+.
|
||||
func InstrumentHandlerDuration(obs prometheus.ObserverVec, next http.Handler) http.HandlerFunc {
|
||||
func InstrumentHandlerDuration(obs prometheus.ObserverVec, next http.Handler, opts ...Option) http.HandlerFunc {
|
||||
mwOpts := &option{}
|
||||
for _, o := range opts {
|
||||
o(mwOpts)
|
||||
}
|
||||
|
||||
code, method := checkLabels(obs)
|
||||
|
||||
if code {
|
||||
@@ -67,57 +75,70 @@ func InstrumentHandlerDuration(obs prometheus.ObserverVec, next http.Handler) ht
|
||||
d := newDelegator(w, nil)
|
||||
next.ServeHTTP(d, r)
|
||||
|
||||
obs.With(labels(code, method, r.Method, d.Status())).Observe(time.Since(now).Seconds())
|
||||
obs.With(labels(code, method, r.Method, d.Status(), mwOpts.extraMethods...)).Observe(time.Since(now).Seconds())
|
||||
})
|
||||
}
|
||||
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
now := time.Now()
|
||||
next.ServeHTTP(w, r)
|
||||
obs.With(labels(code, method, r.Method, 0)).Observe(time.Since(now).Seconds())
|
||||
obs.With(labels(code, method, r.Method, 0, mwOpts.extraMethods...)).Observe(time.Since(now).Seconds())
|
||||
})
|
||||
}
|
||||
|
||||
// InstrumentHandlerCounter is a middleware that wraps the provided http.Handler
|
||||
// to observe the request result with the provided CounterVec. The CounterVec
|
||||
// must have zero, one, or two non-const non-curried labels. For those, the only
|
||||
// allowed label names are "code" and "method". The function panics
|
||||
// otherwise. Partitioning of the CounterVec happens by HTTP status code and/or
|
||||
// HTTP method if the respective instance label names are present in the
|
||||
// CounterVec. For unpartitioned counting, use a CounterVec with zero labels.
|
||||
// to observe the request result with the provided CounterVec. The CounterVec
|
||||
// must have valid metric and label names and must have zero, one, or two
|
||||
// non-const non-curried labels. For those, the only allowed label names are
|
||||
// "code" and "method". The function panics otherwise. For the "method"
|
||||
// label a predefined default label value set is used to filter given values.
|
||||
// Values besides predefined values will count as `unknown` method.
|
||||
// `WithExtraMethods` can be used to add more methods to the set. Partitioning of the
|
||||
// CounterVec happens by HTTP status code and/or HTTP method if the respective
|
||||
// instance label names are present in the CounterVec. For unpartitioned
|
||||
// counting, use a CounterVec with zero labels.
|
||||
//
|
||||
// If the wrapped Handler does not set a status code, a status code of 200 is assumed.
|
||||
//
|
||||
// If the wrapped Handler panics, the Counter is not incremented.
|
||||
//
|
||||
// See the example for InstrumentHandlerDuration for example usage.
|
||||
func InstrumentHandlerCounter(counter *prometheus.CounterVec, next http.Handler) http.HandlerFunc {
|
||||
func InstrumentHandlerCounter(counter *prometheus.CounterVec, next http.Handler, opts ...Option) http.HandlerFunc {
|
||||
mwOpts := &option{}
|
||||
for _, o := range opts {
|
||||
o(mwOpts)
|
||||
}
|
||||
|
||||
code, method := checkLabels(counter)
|
||||
|
||||
if code {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
d := newDelegator(w, nil)
|
||||
next.ServeHTTP(d, r)
|
||||
counter.With(labels(code, method, r.Method, d.Status())).Inc()
|
||||
counter.With(labels(code, method, r.Method, d.Status(), mwOpts.extraMethods...)).Inc()
|
||||
})
|
||||
}
|
||||
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
next.ServeHTTP(w, r)
|
||||
counter.With(labels(code, method, r.Method, 0)).Inc()
|
||||
counter.With(labels(code, method, r.Method, 0, mwOpts.extraMethods...)).Inc()
|
||||
})
|
||||
}
|
||||
|
||||
// InstrumentHandlerTimeToWriteHeader is a middleware that wraps the provided
|
||||
// http.Handler to observe with the provided ObserverVec the request duration
|
||||
// until the response headers are written. The ObserverVec must have zero, one,
|
||||
// or two non-const non-curried labels. For those, the only allowed label names
|
||||
// are "code" and "method". The function panics otherwise. The Observe method of
|
||||
// the Observer in the ObserverVec is called with the request duration in
|
||||
// seconds. Partitioning happens by HTTP status code and/or HTTP method if the
|
||||
// respective instance label names are present in the ObserverVec. For
|
||||
// unpartitioned observations, use an ObserverVec with zero labels. Note that
|
||||
// partitioning of Histograms is expensive and should be used judiciously.
|
||||
// until the response headers are written. The ObserverVec must have valid
|
||||
// metric and label names and must have zero, one, or two non-const non-curried
|
||||
// labels. For those, the only allowed label names are "code" and "method". The
|
||||
// function panics otherwise. For the "method" label a predefined default label
|
||||
// value set is used to filter given values. Values besides predefined values
|
||||
// will count as `unknown` method.`WithExtraMethods` can be used to add more
|
||||
// methods to the set. The Observe method of the Observer in the
|
||||
// ObserverVec is called with the request duration in seconds. Partitioning
|
||||
// happens by HTTP status code and/or HTTP method if the respective instance
|
||||
// label names are present in the ObserverVec. For unpartitioned observations,
|
||||
// use an ObserverVec with zero labels. Note that partitioning of Histograms is
|
||||
// expensive and should be used judiciously.
|
||||
//
|
||||
// If the wrapped Handler panics before calling WriteHeader, no value is
|
||||
// reported.
|
||||
@@ -126,35 +147,48 @@ func InstrumentHandlerCounter(counter *prometheus.CounterVec, next http.Handler)
|
||||
// if used with Go1.9+.
|
||||
//
|
||||
// See the example for InstrumentHandlerDuration for example usage.
|
||||
func InstrumentHandlerTimeToWriteHeader(obs prometheus.ObserverVec, next http.Handler) http.HandlerFunc {
|
||||
func InstrumentHandlerTimeToWriteHeader(obs prometheus.ObserverVec, next http.Handler, opts ...Option) http.HandlerFunc {
|
||||
mwOpts := &option{}
|
||||
for _, o := range opts {
|
||||
o(mwOpts)
|
||||
}
|
||||
|
||||
code, method := checkLabels(obs)
|
||||
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
now := time.Now()
|
||||
d := newDelegator(w, func(status int) {
|
||||
obs.With(labels(code, method, r.Method, status)).Observe(time.Since(now).Seconds())
|
||||
obs.With(labels(code, method, r.Method, status, mwOpts.extraMethods...)).Observe(time.Since(now).Seconds())
|
||||
})
|
||||
next.ServeHTTP(d, r)
|
||||
})
|
||||
}
|
||||
|
||||
// InstrumentHandlerRequestSize is a middleware that wraps the provided
|
||||
// http.Handler to observe the request size with the provided ObserverVec. The
|
||||
// ObserverVec must have zero, one, or two non-const non-curried labels. For
|
||||
// those, the only allowed label names are "code" and "method". The function
|
||||
// panics otherwise. The Observe method of the Observer in the ObserverVec is
|
||||
// called with the request size in bytes. Partitioning happens by HTTP status
|
||||
// code and/or HTTP method if the respective instance label names are present in
|
||||
// the ObserverVec. For unpartitioned observations, use an ObserverVec with zero
|
||||
// labels. Note that partitioning of Histograms is expensive and should be used
|
||||
// judiciously.
|
||||
// http.Handler to observe the request size with the provided ObserverVec. The
|
||||
// ObserverVec must have valid metric and label names and must have zero, one,
|
||||
// or two non-const non-curried labels. For those, the only allowed label names
|
||||
// are "code" and "method". The function panics otherwise. For the "method"
|
||||
// label a predefined default label value set is used to filter given values.
|
||||
// Values besides predefined values will count as `unknown` method.
|
||||
// `WithExtraMethods` can be used to add more methods to the set. The Observe
|
||||
// method of the Observer in the ObserverVec is called with the request size in
|
||||
// bytes. Partitioning happens by HTTP status code and/or HTTP method if the
|
||||
// respective instance label names are present in the ObserverVec. For
|
||||
// unpartitioned observations, use an ObserverVec with zero labels. Note that
|
||||
// partitioning of Histograms is expensive and should be used judiciously.
|
||||
//
|
||||
// If the wrapped Handler does not set a status code, a status code of 200 is assumed.
|
||||
//
|
||||
// If the wrapped Handler panics, no values are reported.
|
||||
//
|
||||
// See the example for InstrumentHandlerDuration for example usage.
|
||||
func InstrumentHandlerRequestSize(obs prometheus.ObserverVec, next http.Handler) http.HandlerFunc {
|
||||
func InstrumentHandlerRequestSize(obs prometheus.ObserverVec, next http.Handler, opts ...Option) http.HandlerFunc {
|
||||
mwOpts := &option{}
|
||||
for _, o := range opts {
|
||||
o(mwOpts)
|
||||
}
|
||||
|
||||
code, method := checkLabels(obs)
|
||||
|
||||
if code {
|
||||
@@ -162,42 +196,56 @@ func InstrumentHandlerRequestSize(obs prometheus.ObserverVec, next http.Handler)
|
||||
d := newDelegator(w, nil)
|
||||
next.ServeHTTP(d, r)
|
||||
size := computeApproximateRequestSize(r)
|
||||
obs.With(labels(code, method, r.Method, d.Status())).Observe(float64(size))
|
||||
obs.With(labels(code, method, r.Method, d.Status(), mwOpts.extraMethods...)).Observe(float64(size))
|
||||
})
|
||||
}
|
||||
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
next.ServeHTTP(w, r)
|
||||
size := computeApproximateRequestSize(r)
|
||||
obs.With(labels(code, method, r.Method, 0)).Observe(float64(size))
|
||||
obs.With(labels(code, method, r.Method, 0, mwOpts.extraMethods...)).Observe(float64(size))
|
||||
})
|
||||
}
|
||||
|
||||
// InstrumentHandlerResponseSize is a middleware that wraps the provided
|
||||
// http.Handler to observe the response size with the provided ObserverVec. The
|
||||
// ObserverVec must have zero, one, or two non-const non-curried labels. For
|
||||
// those, the only allowed label names are "code" and "method". The function
|
||||
// panics otherwise. The Observe method of the Observer in the ObserverVec is
|
||||
// called with the response size in bytes. Partitioning happens by HTTP status
|
||||
// code and/or HTTP method if the respective instance label names are present in
|
||||
// the ObserverVec. For unpartitioned observations, use an ObserverVec with zero
|
||||
// labels. Note that partitioning of Histograms is expensive and should be used
|
||||
// judiciously.
|
||||
// http.Handler to observe the response size with the provided ObserverVec. The
|
||||
// ObserverVec must have valid metric and label names and must have zero, one,
|
||||
// or two non-const non-curried labels. For those, the only allowed label names
|
||||
// are "code" and "method". The function panics otherwise. For the "method"
|
||||
// label a predefined default label value set is used to filter given values.
|
||||
// Values besides predefined values will count as `unknown` method.
|
||||
// `WithExtraMethods` can be used to add more methods to the set. The Observe
|
||||
// method of the Observer in the ObserverVec is called with the response size in
|
||||
// bytes. Partitioning happens by HTTP status code and/or HTTP method if the
|
||||
// respective instance label names are present in the ObserverVec. For
|
||||
// unpartitioned observations, use an ObserverVec with zero labels. Note that
|
||||
// partitioning of Histograms is expensive and should be used judiciously.
|
||||
//
|
||||
// If the wrapped Handler does not set a status code, a status code of 200 is assumed.
|
||||
//
|
||||
// If the wrapped Handler panics, no values are reported.
|
||||
//
|
||||
// See the example for InstrumentHandlerDuration for example usage.
|
||||
func InstrumentHandlerResponseSize(obs prometheus.ObserverVec, next http.Handler) http.Handler {
|
||||
func InstrumentHandlerResponseSize(obs prometheus.ObserverVec, next http.Handler, opts ...Option) http.Handler {
|
||||
mwOpts := &option{}
|
||||
for _, o := range opts {
|
||||
o(mwOpts)
|
||||
}
|
||||
|
||||
code, method := checkLabels(obs)
|
||||
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
d := newDelegator(w, nil)
|
||||
next.ServeHTTP(d, r)
|
||||
obs.With(labels(code, method, r.Method, d.Status())).Observe(float64(d.Written()))
|
||||
obs.With(labels(code, method, r.Method, d.Status(), mwOpts.extraMethods...)).Observe(float64(d.Written()))
|
||||
})
|
||||
}
|
||||
|
||||
// checkLabels returns whether the provided Collector has a non-const,
|
||||
// non-curried label named "code" and/or "method". It panics if the provided
|
||||
// Collector does not have a Desc or has more than one Desc or its Desc is
|
||||
// invalid. It also panics if the Collector has any non-const, non-curried
|
||||
// labels that are not named "code" or "method".
|
||||
func checkLabels(c prometheus.Collector) (code bool, method bool) {
|
||||
// TODO(beorn7): Remove this hacky way to check for instance labels
|
||||
// once Descriptors can have their dimensionality queried.
|
||||
@@ -225,6 +273,10 @@ func checkLabels(c prometheus.Collector) (code bool, method bool) {
|
||||
|
||||
close(descc)
|
||||
|
||||
// Make sure the Collector has a valid Desc by registering it with a
|
||||
// temporary registry.
|
||||
prometheus.NewRegistry().MustRegister(c)
|
||||
|
||||
// Create a ConstMetric with the Desc. Since we don't know how many
|
||||
// variable labels there are, try for as long as it needs.
|
||||
for err := errors.New("dummy"); err != nil; lvs = append(lvs, magicString) {
|
||||
@@ -279,7 +331,7 @@ func isLabelCurried(c prometheus.Collector, label string) bool {
|
||||
// unnecessary allocations on each request.
|
||||
var emptyLabels = prometheus.Labels{}
|
||||
|
||||
func labels(code, method bool, reqMethod string, status int) prometheus.Labels {
|
||||
func labels(code, method bool, reqMethod string, status int, extraMethods ...string) prometheus.Labels {
|
||||
if !(code || method) {
|
||||
return emptyLabels
|
||||
}
|
||||
@@ -289,7 +341,7 @@ func labels(code, method bool, reqMethod string, status int) prometheus.Labels {
|
||||
labels["code"] = sanitizeCode(status)
|
||||
}
|
||||
if method {
|
||||
labels["method"] = sanitizeMethod(reqMethod)
|
||||
labels["method"] = sanitizeMethod(reqMethod, extraMethods...)
|
||||
}
|
||||
|
||||
return labels
|
||||
@@ -319,7 +371,12 @@ func computeApproximateRequestSize(r *http.Request) int {
|
||||
return s
|
||||
}
|
||||
|
||||
func sanitizeMethod(m string) string {
|
||||
// If the wrapped http.Handler has a known method, it will be sanitized and returned.
|
||||
// Otherwise, "unknown" will be returned. The known method list can be extended
|
||||
// as needed by using extraMethods parameter.
|
||||
func sanitizeMethod(m string, extraMethods ...string) string {
|
||||
// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods for
|
||||
// the methods chosen as default.
|
||||
switch m {
|
||||
case "GET", "get":
|
||||
return "get"
|
||||
@@ -337,15 +394,25 @@ func sanitizeMethod(m string) string {
|
||||
return "options"
|
||||
case "NOTIFY", "notify":
|
||||
return "notify"
|
||||
case "TRACE", "trace":
|
||||
return "trace"
|
||||
case "PATCH", "patch":
|
||||
return "patch"
|
||||
default:
|
||||
return strings.ToLower(m)
|
||||
for _, method := range extraMethods {
|
||||
if strings.EqualFold(m, method) {
|
||||
return strings.ToLower(m)
|
||||
}
|
||||
}
|
||||
return "unknown"
|
||||
}
|
||||
}
|
||||
|
||||
// If the wrapped http.Handler has not set a status code, i.e. the value is
|
||||
// currently 0, santizeCode will return 200, for consistency with behavior in
|
||||
// currently 0, sanitizeCode will return 200, for consistency with behavior in
|
||||
// the stdlib.
|
||||
func sanitizeCode(s int) string {
|
||||
// See for accepted codes https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
|
||||
switch s {
|
||||
case 100:
|
||||
return "100"
|
||||
@@ -442,6 +509,9 @@ func sanitizeCode(s int) string {
|
||||
return "511"
|
||||
|
||||
default:
|
||||
return strconv.Itoa(s)
|
||||
if s >= 100 && s <= 599 {
|
||||
return strconv.Itoa(s)
|
||||
}
|
||||
return "unknown"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2019 The Prometheus Authors
|
||||
// Copyright 2022 The Prometheus Authors
|
||||
// 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
|
||||
@@ -11,19 +11,21 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// +build go1.12
|
||||
package promhttp
|
||||
|
||||
package prometheus
|
||||
// Option are used to configure a middleware or round tripper..
|
||||
type Option func(*option)
|
||||
|
||||
import "runtime/debug"
|
||||
|
||||
// readBuildInfo is a wrapper around debug.ReadBuildInfo for Go 1.12+.
|
||||
func readBuildInfo() (path, version, sum string) {
|
||||
path, version, sum = "unknown", "unknown", "unknown"
|
||||
if bi, ok := debug.ReadBuildInfo(); ok {
|
||||
path = bi.Main.Path
|
||||
version = bi.Main.Version
|
||||
sum = bi.Main.Sum
|
||||
}
|
||||
return
|
||||
type option struct {
|
||||
extraMethods []string
|
||||
}
|
||||
|
||||
// WithExtraMethods adds additional HTTP methods to the list of allowed methods.
|
||||
// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods for the default list.
|
||||
//
|
||||
// See the example for ExampleInstrumentHandlerWithExtraMethods for example usage.
|
||||
func WithExtraMethods(methods ...string) Option {
|
||||
return func(o *option) {
|
||||
o.extraMethods = methods
|
||||
}
|
||||
}
|
||||
4
vendor/github.com/prometheus/client_golang/prometheus/registry.go
generated
vendored
4
vendor/github.com/prometheus/client_golang/prometheus/registry.go
generated
vendored
@@ -26,7 +26,7 @@ import (
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/cespare/xxhash/v2"
|
||||
//lint:ignore SA1019 Need to keep deprecated package for compatibility.
|
||||
//nolint:staticcheck // Ignore SA1019. Need to keep deprecated package for compatibility.
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/prometheus/common/expfmt"
|
||||
|
||||
@@ -215,6 +215,8 @@ func (err AlreadyRegisteredError) Error() string {
|
||||
// by a Gatherer to report multiple errors during MetricFamily gathering.
|
||||
type MultiError []error
|
||||
|
||||
// Error formats the contained errors as a bullet point list, preceded by the
|
||||
// total number of errors. Note that this results in a multi-line string.
|
||||
func (errs MultiError) Error() string {
|
||||
if len(errs) == 0 {
|
||||
return ""
|
||||
|
||||
39
vendor/github.com/prometheus/client_golang/prometheus/summary.go
generated
vendored
39
vendor/github.com/prometheus/client_golang/prometheus/summary.go
generated
vendored
@@ -23,7 +23,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/beorn7/perks/quantile"
|
||||
//lint:ignore SA1019 Need to keep deprecated package for compatibility.
|
||||
//nolint:staticcheck // Ignore SA1019. Need to keep deprecated package for compatibility.
|
||||
"github.com/golang/protobuf/proto"
|
||||
|
||||
dto "github.com/prometheus/client_model/go"
|
||||
@@ -55,7 +55,12 @@ type Summary interface {
|
||||
Metric
|
||||
Collector
|
||||
|
||||
// Observe adds a single observation to the summary.
|
||||
// Observe adds a single observation to the summary. Observations are
|
||||
// usually positive or zero. Negative observations are accepted but
|
||||
// prevent current versions of Prometheus from properly detecting
|
||||
// counter resets in the sum of observations. See
|
||||
// https://prometheus.io/docs/practices/histograms/#count-and-sum-of-observations
|
||||
// for details.
|
||||
Observe(float64)
|
||||
}
|
||||
|
||||
@@ -110,7 +115,7 @@ type SummaryOpts struct {
|
||||
// better covered by target labels set by the scraping Prometheus
|
||||
// server, or by one specific metric (e.g. a build_info or a
|
||||
// machine_role metric). See also
|
||||
// https://prometheus.io/docs/instrumenting/writing_exporters/#target-labels,-not-static-scraped-labels
|
||||
// https://prometheus.io/docs/instrumenting/writing_exporters/#target-labels-not-static-scraped-labels
|
||||
ConstLabels Labels
|
||||
|
||||
// Objectives defines the quantile rank estimates with their respective
|
||||
@@ -121,7 +126,9 @@ type SummaryOpts struct {
|
||||
Objectives map[float64]float64
|
||||
|
||||
// MaxAge defines the duration for which an observation stays relevant
|
||||
// for the summary. Must be positive. The default value is DefMaxAge.
|
||||
// for the summary. Only applies to pre-calculated quantiles, does not
|
||||
// apply to _sum and _count. Must be positive. The default value is
|
||||
// DefMaxAge.
|
||||
MaxAge time.Duration
|
||||
|
||||
// AgeBuckets is the number of buckets used to exclude observations that
|
||||
@@ -208,7 +215,7 @@ func newSummary(desc *Desc, opts SummaryOpts, labelValues ...string) Summary {
|
||||
// Use the lock-free implementation of a Summary without objectives.
|
||||
s := &noObjectivesSummary{
|
||||
desc: desc,
|
||||
labelPairs: makeLabelPairs(desc, labelValues),
|
||||
labelPairs: MakeLabelPairs(desc, labelValues),
|
||||
counts: [2]*summaryCounts{{}, {}},
|
||||
}
|
||||
s.init(s) // Init self-collection.
|
||||
@@ -221,7 +228,7 @@ func newSummary(desc *Desc, opts SummaryOpts, labelValues ...string) Summary {
|
||||
objectives: opts.Objectives,
|
||||
sortedObjectives: make([]float64, 0, len(opts.Objectives)),
|
||||
|
||||
labelPairs: makeLabelPairs(desc, labelValues),
|
||||
labelPairs: MakeLabelPairs(desc, labelValues),
|
||||
|
||||
hotBuf: make([]float64, 0, opts.BufCap),
|
||||
coldBuf: make([]float64, 0, opts.BufCap),
|
||||
@@ -513,7 +520,7 @@ func (s quantSort) Less(i, j int) bool {
|
||||
// (e.g. HTTP request latencies, partitioned by status code and method). Create
|
||||
// instances with NewSummaryVec.
|
||||
type SummaryVec struct {
|
||||
*metricVec
|
||||
*MetricVec
|
||||
}
|
||||
|
||||
// NewSummaryVec creates a new SummaryVec based on the provided SummaryOpts and
|
||||
@@ -535,14 +542,14 @@ func NewSummaryVec(opts SummaryOpts, labelNames []string) *SummaryVec {
|
||||
opts.ConstLabels,
|
||||
)
|
||||
return &SummaryVec{
|
||||
metricVec: newMetricVec(desc, func(lvs ...string) Metric {
|
||||
MetricVec: NewMetricVec(desc, func(lvs ...string) Metric {
|
||||
return newSummary(desc, opts, lvs...)
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
// GetMetricWithLabelValues returns the Summary for the given slice of label
|
||||
// values (same order as the VariableLabels in Desc). If that combination of
|
||||
// values (same order as the variable labels in Desc). If that combination of
|
||||
// label values is accessed for the first time, a new Summary is created.
|
||||
//
|
||||
// It is possible to call this method without using the returned Summary to only
|
||||
@@ -557,7 +564,7 @@ func NewSummaryVec(opts SummaryOpts, labelNames []string) *SummaryVec {
|
||||
// example.
|
||||
//
|
||||
// An error is returned if the number of label values is not the same as the
|
||||
// number of VariableLabels in Desc (minus any curried labels).
|
||||
// number of variable labels in Desc (minus any curried labels).
|
||||
//
|
||||
// Note that for more than one label value, this method is prone to mistakes
|
||||
// caused by an incorrect order of arguments. Consider GetMetricWith(Labels) as
|
||||
@@ -566,7 +573,7 @@ func NewSummaryVec(opts SummaryOpts, labelNames []string) *SummaryVec {
|
||||
// with a performance overhead (for creating and processing the Labels map).
|
||||
// See also the GaugeVec example.
|
||||
func (v *SummaryVec) GetMetricWithLabelValues(lvs ...string) (Observer, error) {
|
||||
metric, err := v.metricVec.getMetricWithLabelValues(lvs...)
|
||||
metric, err := v.MetricVec.GetMetricWithLabelValues(lvs...)
|
||||
if metric != nil {
|
||||
return metric.(Observer), err
|
||||
}
|
||||
@@ -574,19 +581,19 @@ func (v *SummaryVec) GetMetricWithLabelValues(lvs ...string) (Observer, error) {
|
||||
}
|
||||
|
||||
// GetMetricWith returns the Summary for the given Labels map (the label names
|
||||
// must match those of the VariableLabels in Desc). If that label map is
|
||||
// must match those of the variable labels in Desc). If that label map is
|
||||
// accessed for the first time, a new Summary is created. Implications of
|
||||
// creating a Summary without using it and keeping the Summary for later use are
|
||||
// the same as for GetMetricWithLabelValues.
|
||||
//
|
||||
// An error is returned if the number and names of the Labels are inconsistent
|
||||
// with those of the VariableLabels in Desc (minus any curried labels).
|
||||
// with those of the variable labels in Desc (minus any curried labels).
|
||||
//
|
||||
// This method is used for the same purpose as
|
||||
// GetMetricWithLabelValues(...string). See there for pros and cons of the two
|
||||
// methods.
|
||||
func (v *SummaryVec) GetMetricWith(labels Labels) (Observer, error) {
|
||||
metric, err := v.metricVec.getMetricWith(labels)
|
||||
metric, err := v.MetricVec.GetMetricWith(labels)
|
||||
if metric != nil {
|
||||
return metric.(Observer), err
|
||||
}
|
||||
@@ -630,7 +637,7 @@ func (v *SummaryVec) With(labels Labels) Observer {
|
||||
// registered with a given registry (usually the uncurried version). The Reset
|
||||
// method deletes all metrics, even if called on a curried vector.
|
||||
func (v *SummaryVec) CurryWith(labels Labels) (ObserverVec, error) {
|
||||
vec, err := v.curryWith(labels)
|
||||
vec, err := v.MetricVec.CurryWith(labels)
|
||||
if vec != nil {
|
||||
return &SummaryVec{vec}, err
|
||||
}
|
||||
@@ -716,7 +723,7 @@ func NewConstSummary(
|
||||
count: count,
|
||||
sum: sum,
|
||||
quantiles: quantiles,
|
||||
labelPairs: makeLabelPairs(desc, labelValues),
|
||||
labelPairs: MakeLabelPairs(desc, labelValues),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
15
vendor/github.com/prometheus/client_golang/prometheus/value.go
generated
vendored
15
vendor/github.com/prometheus/client_golang/prometheus/value.go
generated
vendored
@@ -19,7 +19,7 @@ import (
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
|
||||
//lint:ignore SA1019 Need to keep deprecated package for compatibility.
|
||||
//nolint:staticcheck // Ignore SA1019. Need to keep deprecated package for compatibility.
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/golang/protobuf/ptypes"
|
||||
|
||||
@@ -63,7 +63,7 @@ func newValueFunc(desc *Desc, valueType ValueType, function func() float64) *val
|
||||
desc: desc,
|
||||
valType: valueType,
|
||||
function: function,
|
||||
labelPairs: makeLabelPairs(desc, nil),
|
||||
labelPairs: MakeLabelPairs(desc, nil),
|
||||
}
|
||||
result.init(result)
|
||||
return result
|
||||
@@ -95,7 +95,7 @@ func NewConstMetric(desc *Desc, valueType ValueType, value float64, labelValues
|
||||
desc: desc,
|
||||
valType: valueType,
|
||||
val: value,
|
||||
labelPairs: makeLabelPairs(desc, labelValues),
|
||||
labelPairs: MakeLabelPairs(desc, labelValues),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -145,7 +145,14 @@ func populateMetric(
|
||||
return nil
|
||||
}
|
||||
|
||||
func makeLabelPairs(desc *Desc, labelValues []string) []*dto.LabelPair {
|
||||
// MakeLabelPairs is a helper function to create protobuf LabelPairs from the
|
||||
// variable and constant labels in the provided Desc. The values for the
|
||||
// variable labels are defined by the labelValues slice, which must be in the
|
||||
// same order as the corresponding variable labels in the Desc.
|
||||
//
|
||||
// This function is only needed for custom Metric implementations. See MetricVec
|
||||
// example.
|
||||
func MakeLabelPairs(desc *Desc, labelValues []string) []*dto.LabelPair {
|
||||
totalLen := len(desc.variableLabels) + len(desc.constLabelPairs)
|
||||
if totalLen == 0 {
|
||||
// Super fast path.
|
||||
|
||||
112
vendor/github.com/prometheus/client_golang/prometheus/vec.go
generated
vendored
112
vendor/github.com/prometheus/client_golang/prometheus/vec.go
generated
vendored
@@ -20,12 +20,20 @@ import (
|
||||
"github.com/prometheus/common/model"
|
||||
)
|
||||
|
||||
// metricVec is a Collector to bundle metrics of the same name that differ in
|
||||
// their label values. metricVec is not used directly (and therefore
|
||||
// unexported). It is used as a building block for implementations of vectors of
|
||||
// a given metric type, like GaugeVec, CounterVec, SummaryVec, and HistogramVec.
|
||||
// It also handles label currying.
|
||||
type metricVec struct {
|
||||
// MetricVec is a Collector to bundle metrics of the same name that differ in
|
||||
// their label values. MetricVec is not used directly but as a building block
|
||||
// for implementations of vectors of a given metric type, like GaugeVec,
|
||||
// CounterVec, SummaryVec, and HistogramVec. It is exported so that it can be
|
||||
// used for custom Metric implementations.
|
||||
//
|
||||
// To create a FooVec for custom Metric Foo, embed a pointer to MetricVec in
|
||||
// FooVec and initialize it with NewMetricVec. Implement wrappers for
|
||||
// GetMetricWithLabelValues and GetMetricWith that return (Foo, error) rather
|
||||
// than (Metric, error). Similarly, create a wrapper for CurryWith that returns
|
||||
// (*FooVec, error) rather than (*MetricVec, error). It is recommended to also
|
||||
// add the convenience methods WithLabelValues, With, and MustCurryWith, which
|
||||
// panic instead of returning errors. See also the MetricVec example.
|
||||
type MetricVec struct {
|
||||
*metricMap
|
||||
|
||||
curry []curriedLabelValue
|
||||
@@ -35,9 +43,9 @@ type metricVec struct {
|
||||
hashAddByte func(h uint64, b byte) uint64
|
||||
}
|
||||
|
||||
// newMetricVec returns an initialized metricVec.
|
||||
func newMetricVec(desc *Desc, newMetric func(lvs ...string) Metric) *metricVec {
|
||||
return &metricVec{
|
||||
// NewMetricVec returns an initialized metricVec.
|
||||
func NewMetricVec(desc *Desc, newMetric func(lvs ...string) Metric) *MetricVec {
|
||||
return &MetricVec{
|
||||
metricMap: &metricMap{
|
||||
metrics: map[uint64][]metricWithLabelValues{},
|
||||
desc: desc,
|
||||
@@ -63,7 +71,7 @@ func newMetricVec(desc *Desc, newMetric func(lvs ...string) Metric) *metricVec {
|
||||
// latter has a much more readable (albeit more verbose) syntax, but it comes
|
||||
// with a performance overhead (for creating and processing the Labels map).
|
||||
// See also the CounterVec example.
|
||||
func (m *metricVec) DeleteLabelValues(lvs ...string) bool {
|
||||
func (m *MetricVec) DeleteLabelValues(lvs ...string) bool {
|
||||
h, err := m.hashLabelValues(lvs)
|
||||
if err != nil {
|
||||
return false
|
||||
@@ -82,7 +90,7 @@ func (m *metricVec) DeleteLabelValues(lvs ...string) bool {
|
||||
//
|
||||
// This method is used for the same purpose as DeleteLabelValues(...string). See
|
||||
// there for pros and cons of the two methods.
|
||||
func (m *metricVec) Delete(labels Labels) bool {
|
||||
func (m *MetricVec) Delete(labels Labels) bool {
|
||||
h, err := m.hashLabels(labels)
|
||||
if err != nil {
|
||||
return false
|
||||
@@ -95,15 +103,32 @@ func (m *metricVec) Delete(labels Labels) bool {
|
||||
// show up in GoDoc.
|
||||
|
||||
// Describe implements Collector.
|
||||
func (m *metricVec) Describe(ch chan<- *Desc) { m.metricMap.Describe(ch) }
|
||||
func (m *MetricVec) Describe(ch chan<- *Desc) { m.metricMap.Describe(ch) }
|
||||
|
||||
// Collect implements Collector.
|
||||
func (m *metricVec) Collect(ch chan<- Metric) { m.metricMap.Collect(ch) }
|
||||
func (m *MetricVec) Collect(ch chan<- Metric) { m.metricMap.Collect(ch) }
|
||||
|
||||
// Reset deletes all metrics in this vector.
|
||||
func (m *metricVec) Reset() { m.metricMap.Reset() }
|
||||
func (m *MetricVec) Reset() { m.metricMap.Reset() }
|
||||
|
||||
func (m *metricVec) curryWith(labels Labels) (*metricVec, error) {
|
||||
// CurryWith returns a vector curried with the provided labels, i.e. the
|
||||
// returned vector has those labels pre-set for all labeled operations performed
|
||||
// on it. The cardinality of the curried vector is reduced accordingly. The
|
||||
// order of the remaining labels stays the same (just with the curried labels
|
||||
// taken out of the sequence – which is relevant for the
|
||||
// (GetMetric)WithLabelValues methods). It is possible to curry a curried
|
||||
// vector, but only with labels not yet used for currying before.
|
||||
//
|
||||
// The metrics contained in the MetricVec are shared between the curried and
|
||||
// uncurried vectors. They are just accessed differently. Curried and uncurried
|
||||
// vectors behave identically in terms of collection. Only one must be
|
||||
// registered with a given registry (usually the uncurried version). The Reset
|
||||
// method deletes all metrics, even if called on a curried vector.
|
||||
//
|
||||
// Note that CurryWith is usually not called directly but through a wrapper
|
||||
// around MetricVec, implementing a vector for a specific Metric
|
||||
// implementation, for example GaugeVec.
|
||||
func (m *MetricVec) CurryWith(labels Labels) (*MetricVec, error) {
|
||||
var (
|
||||
newCurry []curriedLabelValue
|
||||
oldCurry = m.curry
|
||||
@@ -128,7 +153,7 @@ func (m *metricVec) curryWith(labels Labels) (*metricVec, error) {
|
||||
return nil, fmt.Errorf("%d unknown label(s) found during currying", l)
|
||||
}
|
||||
|
||||
return &metricVec{
|
||||
return &MetricVec{
|
||||
metricMap: m.metricMap,
|
||||
curry: newCurry,
|
||||
hashAdd: m.hashAdd,
|
||||
@@ -136,7 +161,34 @@ func (m *metricVec) curryWith(labels Labels) (*metricVec, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (m *metricVec) getMetricWithLabelValues(lvs ...string) (Metric, error) {
|
||||
// GetMetricWithLabelValues returns the Metric for the given slice of label
|
||||
// values (same order as the variable labels in Desc). If that combination of
|
||||
// label values is accessed for the first time, a new Metric is created (by
|
||||
// calling the newMetric function provided during construction of the
|
||||
// MetricVec).
|
||||
//
|
||||
// It is possible to call this method without using the returned Metric to only
|
||||
// create the new Metric but leave it in its initial state.
|
||||
//
|
||||
// Keeping the Metric for later use is possible (and should be considered if
|
||||
// performance is critical), but keep in mind that Reset, DeleteLabelValues and
|
||||
// Delete can be used to delete the Metric from the MetricVec. In that case, the
|
||||
// Metric will still exist, but it will not be exported anymore, even if a
|
||||
// Metric with the same label values is created later.
|
||||
//
|
||||
// An error is returned if the number of label values is not the same as the
|
||||
// number of variable labels in Desc (minus any curried labels).
|
||||
//
|
||||
// Note that for more than one label value, this method is prone to mistakes
|
||||
// caused by an incorrect order of arguments. Consider GetMetricWith(Labels) as
|
||||
// an alternative to avoid that type of mistake. For higher label numbers, the
|
||||
// latter has a much more readable (albeit more verbose) syntax, but it comes
|
||||
// with a performance overhead (for creating and processing the Labels map).
|
||||
//
|
||||
// Note that GetMetricWithLabelValues is usually not called directly but through
|
||||
// a wrapper around MetricVec, implementing a vector for a specific Metric
|
||||
// implementation, for example GaugeVec.
|
||||
func (m *MetricVec) GetMetricWithLabelValues(lvs ...string) (Metric, error) {
|
||||
h, err := m.hashLabelValues(lvs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -145,7 +197,23 @@ func (m *metricVec) getMetricWithLabelValues(lvs ...string) (Metric, error) {
|
||||
return m.metricMap.getOrCreateMetricWithLabelValues(h, lvs, m.curry), nil
|
||||
}
|
||||
|
||||
func (m *metricVec) getMetricWith(labels Labels) (Metric, error) {
|
||||
// GetMetricWith returns the Metric for the given Labels map (the label names
|
||||
// must match those of the variable labels in Desc). If that label map is
|
||||
// accessed for the first time, a new Metric is created. Implications of
|
||||
// creating a Metric without using it and keeping the Metric for later use
|
||||
// are the same as for GetMetricWithLabelValues.
|
||||
//
|
||||
// An error is returned if the number and names of the Labels are inconsistent
|
||||
// with those of the variable labels in Desc (minus any curried labels).
|
||||
//
|
||||
// This method is used for the same purpose as
|
||||
// GetMetricWithLabelValues(...string). See there for pros and cons of the two
|
||||
// methods.
|
||||
//
|
||||
// Note that GetMetricWith is usually not called directly but through a wrapper
|
||||
// around MetricVec, implementing a vector for a specific Metric implementation,
|
||||
// for example GaugeVec.
|
||||
func (m *MetricVec) GetMetricWith(labels Labels) (Metric, error) {
|
||||
h, err := m.hashLabels(labels)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -154,7 +222,7 @@ func (m *metricVec) getMetricWith(labels Labels) (Metric, error) {
|
||||
return m.metricMap.getOrCreateMetricWithLabels(h, labels, m.curry), nil
|
||||
}
|
||||
|
||||
func (m *metricVec) hashLabelValues(vals []string) (uint64, error) {
|
||||
func (m *MetricVec) hashLabelValues(vals []string) (uint64, error) {
|
||||
if err := validateLabelValues(vals, len(m.desc.variableLabels)-len(m.curry)); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -177,7 +245,7 @@ func (m *metricVec) hashLabelValues(vals []string) (uint64, error) {
|
||||
return h, nil
|
||||
}
|
||||
|
||||
func (m *metricVec) hashLabels(labels Labels) (uint64, error) {
|
||||
func (m *MetricVec) hashLabels(labels Labels) (uint64, error) {
|
||||
if err := validateValuesInLabels(labels, len(m.desc.variableLabels)-len(m.curry)); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -276,7 +344,9 @@ func (m *metricMap) deleteByHashWithLabelValues(
|
||||
}
|
||||
|
||||
if len(metrics) > 1 {
|
||||
old := metrics
|
||||
m.metrics[h] = append(metrics[:i], metrics[i+1:]...)
|
||||
old[len(old)-1] = metricWithLabelValues{}
|
||||
} else {
|
||||
delete(m.metrics, h)
|
||||
}
|
||||
@@ -302,7 +372,9 @@ func (m *metricMap) deleteByHashWithLabels(
|
||||
}
|
||||
|
||||
if len(metrics) > 1 {
|
||||
old := metrics
|
||||
m.metrics[h] = append(metrics[:i], metrics[i+1:]...)
|
||||
old[len(old)-1] = metricWithLabelValues{}
|
||||
} else {
|
||||
delete(m.metrics, h)
|
||||
}
|
||||
|
||||
6
vendor/github.com/prometheus/client_golang/prometheus/wrap.go
generated
vendored
6
vendor/github.com/prometheus/client_golang/prometheus/wrap.go
generated
vendored
@@ -17,7 +17,7 @@ import (
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
//lint:ignore SA1019 Need to keep deprecated package for compatibility.
|
||||
//nolint:staticcheck // Ignore SA1019. Need to keep deprecated package for compatibility.
|
||||
"github.com/golang/protobuf/proto"
|
||||
|
||||
dto "github.com/prometheus/client_model/go"
|
||||
@@ -32,7 +32,9 @@ import (
|
||||
// in a no-op Registerer.
|
||||
//
|
||||
// WrapRegistererWith provides a way to add fixed labels to a subset of
|
||||
// Collectors. It should not be used to add fixed labels to all metrics exposed.
|
||||
// Collectors. It should not be used to add fixed labels to all metrics
|
||||
// exposed. See also
|
||||
// https://prometheus.io/docs/instrumenting/writing_exporters/#target-labels-not-static-scraped-labels
|
||||
//
|
||||
// Conflicts between Collectors registered through the original Registerer with
|
||||
// Collectors registered through the wrapping Registerer will still be
|
||||
|
||||
11
vendor/github.com/prometheus/common/expfmt/text_parse.go
generated
vendored
11
vendor/github.com/prometheus/common/expfmt/text_parse.go
generated
vendored
@@ -299,6 +299,17 @@ func (p *TextParser) startLabelName() stateFn {
|
||||
p.parseError(fmt.Sprintf("expected '=' after label name, found %q", p.currentByte))
|
||||
return nil
|
||||
}
|
||||
// Check for duplicate label names.
|
||||
labels := make(map[string]struct{})
|
||||
for _, l := range p.currentMetric.Label {
|
||||
lName := l.GetName()
|
||||
if _, exists := labels[lName]; !exists {
|
||||
labels[lName] = struct{}{}
|
||||
} else {
|
||||
p.parseError(fmt.Sprintf("duplicate label names for metric %q", p.currentMF.GetName()))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return p.startLabelValue
|
||||
}
|
||||
|
||||
|
||||
8
vendor/github.com/prometheus/common/model/labels.go
generated
vendored
8
vendor/github.com/prometheus/common/model/labels.go
generated
vendored
@@ -45,6 +45,14 @@ const (
|
||||
// scrape a target.
|
||||
MetricsPathLabel = "__metrics_path__"
|
||||
|
||||
// ScrapeIntervalLabel is the name of the label that holds the scrape interval
|
||||
// used to scrape a target.
|
||||
ScrapeIntervalLabel = "__scrape_interval__"
|
||||
|
||||
// ScrapeTimeoutLabel is the name of the label that holds the scrape
|
||||
// timeout used to scrape a target.
|
||||
ScrapeTimeoutLabel = "__scrape_timeout__"
|
||||
|
||||
// ReservedLabelPrefix is a prefix which is not legal in user-supplied
|
||||
// label names.
|
||||
ReservedLabelPrefix = "__"
|
||||
|
||||
45
vendor/github.com/prometheus/common/model/time.go
generated
vendored
45
vendor/github.com/prometheus/common/model/time.go
generated
vendored
@@ -14,6 +14,8 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"regexp"
|
||||
@@ -201,13 +203,23 @@ func ParseDuration(durationStr string) (Duration, error) {
|
||||
|
||||
// Parse the match at pos `pos` in the regex and use `mult` to turn that
|
||||
// into ms, then add that value to the total parsed duration.
|
||||
var overflowErr error
|
||||
m := func(pos int, mult time.Duration) {
|
||||
if matches[pos] == "" {
|
||||
return
|
||||
}
|
||||
n, _ := strconv.Atoi(matches[pos])
|
||||
|
||||
// Check if the provided duration overflows time.Duration (> ~ 290years).
|
||||
if n > int((1<<63-1)/mult/time.Millisecond) {
|
||||
overflowErr = errors.New("duration out of range")
|
||||
}
|
||||
d := time.Duration(n) * time.Millisecond
|
||||
dur += d * mult
|
||||
|
||||
if dur < 0 {
|
||||
overflowErr = errors.New("duration out of range")
|
||||
}
|
||||
}
|
||||
|
||||
m(2, 1000*60*60*24*365) // y
|
||||
@@ -218,7 +230,7 @@ func ParseDuration(durationStr string) (Duration, error) {
|
||||
m(12, 1000) // s
|
||||
m(14, 1) // ms
|
||||
|
||||
return Duration(dur), nil
|
||||
return Duration(dur), overflowErr
|
||||
}
|
||||
|
||||
func (d Duration) String() string {
|
||||
@@ -254,6 +266,37 @@ func (d Duration) String() string {
|
||||
return r
|
||||
}
|
||||
|
||||
// MarshalJSON implements the json.Marshaler interface.
|
||||
func (d Duration) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(d.String())
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the json.Unmarshaler interface.
|
||||
func (d *Duration) UnmarshalJSON(bytes []byte) error {
|
||||
var s string
|
||||
if err := json.Unmarshal(bytes, &s); err != nil {
|
||||
return err
|
||||
}
|
||||
dur, err := ParseDuration(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*d = dur
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalText implements the encoding.TextMarshaler interface.
|
||||
func (d *Duration) MarshalText() ([]byte, error) {
|
||||
return []byte(d.String()), nil
|
||||
}
|
||||
|
||||
// UnmarshalText implements the encoding.TextUnmarshaler interface.
|
||||
func (d *Duration) UnmarshalText(text []byte) error {
|
||||
var err error
|
||||
*d, err = ParseDuration(string(text))
|
||||
return err
|
||||
}
|
||||
|
||||
// MarshalYAML implements the yaml.Marshaler interface.
|
||||
func (d Duration) MarshalYAML() (interface{}, error) {
|
||||
return d.String(), nil
|
||||
|
||||
4
vendor/github.com/prometheus/procfs/Makefile.common
generated
vendored
4
vendor/github.com/prometheus/procfs/Makefile.common
generated
vendored
@@ -78,7 +78,7 @@ ifneq ($(shell which gotestsum),)
|
||||
endif
|
||||
endif
|
||||
|
||||
PROMU_VERSION ?= 0.5.0
|
||||
PROMU_VERSION ?= 0.7.0
|
||||
PROMU_URL := https://github.com/prometheus/promu/releases/download/v$(PROMU_VERSION)/promu-$(PROMU_VERSION).$(GO_BUILD_PLATFORM).tar.gz
|
||||
|
||||
GOLANGCI_LINT :=
|
||||
@@ -245,10 +245,12 @@ common-docker-publish: $(PUBLISH_DOCKER_ARCHS)
|
||||
$(PUBLISH_DOCKER_ARCHS): common-docker-publish-%:
|
||||
docker push "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(DOCKER_IMAGE_TAG)"
|
||||
|
||||
DOCKER_MAJOR_VERSION_TAG = $(firstword $(subst ., ,$(shell cat VERSION)))
|
||||
.PHONY: common-docker-tag-latest $(TAG_DOCKER_ARCHS)
|
||||
common-docker-tag-latest: $(TAG_DOCKER_ARCHS)
|
||||
$(TAG_DOCKER_ARCHS): common-docker-tag-latest-%:
|
||||
docker tag "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(DOCKER_IMAGE_TAG)" "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:latest"
|
||||
docker tag "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(DOCKER_IMAGE_TAG)" "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:v$(DOCKER_MAJOR_VERSION_TAG)"
|
||||
|
||||
.PHONY: common-docker-manifest
|
||||
common-docker-manifest:
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user