Compare commits
271 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a587f0165b | |||
|
|
80c7ff0b11 | ||
| 1a635bc659 | |||
| 1811d74890 | |||
|
|
0cab6ed1c7 | ||
|
|
e9b417c311 | ||
| 7f03dbbc14 | |||
| a06485ed93 | |||
| 674a66e710 | |||
| 6017357b9d | |||
| ae282e19ef | |||
|
|
19f0dd286d | ||
|
|
50ea9a9f85 | ||
|
|
feaf18fcf1 | ||
|
|
2174531486 | ||
|
|
e0081f3dbf | ||
| 4a86315749 | |||
|
|
882544647e | ||
| fbe8435884 | |||
|
|
3b5bce2a65 | ||
|
|
d2eb9e8498 | ||
| d8ee2d131a | |||
| bbb056f38c | |||
|
|
e657c5eb56 | ||
|
|
3599852fc1 | ||
|
|
05fe51d96d | ||
|
|
1f7334e3d1 | ||
|
|
dfa8e1ba41 | ||
|
|
9a8582a7a9 | ||
|
|
57ffe4011d | ||
|
|
1d60dd7ce5 | ||
|
|
dc71321648 | ||
|
|
bc4f246a75 | ||
|
|
cc619bdd86 | ||
|
|
784f03875e | ||
|
|
1b7dd14555 | ||
|
|
91603a08b0 | ||
|
|
1766663a78 | ||
|
|
8ca5f31853 | ||
|
|
447e1c57ce | ||
|
|
0e95c1ca9e | ||
|
|
7d093b4340 | ||
|
|
33566bed16 | ||
|
|
e14ffb261a | ||
|
|
1d239647f4 | ||
|
|
f10e2facf8 | ||
|
|
bad1cfaa96 | ||
|
|
bddb9bfd3a | ||
|
|
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 | ||
|
|
4a9fd2965c | ||
|
|
ffd72e8b31 | ||
|
|
8182fc43ab | ||
|
|
63a05ad8e4 | ||
|
|
f6b1ff2afd | ||
|
|
8d996081cc | ||
|
|
15d21c594f | ||
|
|
bfd37e39c5 | ||
|
|
3adb91d9bb | ||
|
|
391f4b66d7 | ||
|
|
a8a281db5a | ||
|
|
05d63e3cbf | ||
|
|
467324dd26 | ||
|
|
8abe55b86c | ||
|
|
8db26ebbb0 | ||
|
|
34831f08a0 | ||
|
|
e9a3b79a90 | ||
|
|
c0a4fb9318 | ||
|
|
fb53af6e7b | ||
|
|
65a36fd896 | ||
|
|
b04ef21071 | ||
|
|
6acc28125f | ||
|
|
08c533ed99 | ||
|
|
45384755bb | ||
|
|
67b2a690c5 | ||
|
|
cdc2d0b16b | ||
|
|
4a66728b79 | ||
|
|
64e4976a43 | ||
|
|
1a45834865 | ||
|
|
2ff55a381b | ||
|
|
7c05f4cbcb | ||
|
|
6e10f3ba76 | ||
|
|
e7b641cb1b | ||
|
|
37187e5199 | ||
|
|
f86dcb1c36 | ||
|
|
ae82196402 | ||
|
|
bebb8e1559 | ||
|
|
a1eda64df3 | ||
|
|
813e1ac2dc | ||
|
|
3111d48330 | ||
|
|
b8dcbde7b7 | ||
|
|
c74cc13a3b | ||
|
|
35cc716fcb | ||
|
|
163873d7a8 | ||
|
|
0e18f17dc9 | ||
|
|
d6ea28ee6b | ||
|
|
093c6be85a | ||
|
|
28faa252a2 | ||
|
|
334df22cfd | ||
|
|
b32963854b | ||
|
|
ad153a3fc2 | ||
|
|
caa0fd6904 | ||
|
|
bd7e1193bf | ||
|
|
7c4d95aa2d | ||
|
|
7f8ffbf914 | ||
|
|
d3c543a42e | ||
|
|
4931e43b67 | ||
|
|
4e26150542 | ||
|
|
bf3d8dd26d | ||
|
|
7c58e2bea2 | ||
|
|
ae5b736f40 | ||
|
|
c1b092e0b1 | ||
|
|
adf7582e8b | ||
|
|
544c2d1e41 | ||
|
|
c3f60c5e24 | ||
|
|
b16246455e | ||
|
|
fdc447761c | ||
|
|
7f5563fa97 | ||
|
|
767381b2a0 | ||
|
|
3ad3e7ea16 | ||
|
|
f6fbc71092 | ||
|
|
0943d420bc | ||
|
|
48cac4fb6c | ||
|
|
c56ec8cd79 | ||
|
|
748d2fd11f | ||
|
|
24eb903b56 | ||
|
|
ebbe30ccf9 | ||
|
|
65e5d60984 | ||
|
|
2279f0b33c | ||
|
|
8fa8d8cb2a | ||
|
|
40d3a9e9ce | ||
|
|
6a8dcfae79 | ||
|
|
85976e1c08 | ||
|
|
4bf3c81e4e | ||
|
|
61e94ed50c | ||
|
|
5c964ef5f7 | ||
|
|
8db0023815 | ||
|
|
060a2e9655 | ||
|
|
d7595f46ff | ||
|
|
d2c11089c8 | ||
|
|
d7fd217770 | ||
|
|
00ada50f06 | ||
|
|
5d88af462f | ||
|
|
eff6ded259 | ||
|
|
2f103128f3 | ||
|
|
a48ac18522 | ||
|
|
1f6d37afed | ||
|
|
5f706e0282 | ||
|
|
1a8855547b | ||
|
|
d0062e71e8 | ||
|
|
353a671c27 | ||
|
|
a46177893c | ||
|
|
65c0ddb289 | ||
|
|
78203aaead | ||
|
|
7cba9ee95f | ||
|
|
7e6b01825b | ||
|
|
79d79ca3d6 | ||
|
|
1e415a30aa | ||
|
|
8461c8822a | ||
|
|
eac4363913 | ||
|
|
a3bf84bcea | ||
|
|
8284ac3169 | ||
|
|
68ef36fafb | ||
|
|
1af99634b4 | ||
|
|
53353c6bac | ||
|
|
a73303d6e6 | ||
|
|
18b0458fa5 | ||
|
|
44eeda157a | ||
|
|
062b29960f | ||
|
|
d4e38c6d85 | ||
|
|
25ba610c6a | ||
|
|
6d2ff0933a | ||
|
|
7a6afb4654 | ||
|
|
f5fdd1008f | ||
|
|
0857c654d2 | ||
|
|
480c3ee027 | ||
|
|
54076af43d | ||
|
|
608f255ab0 | ||
|
|
a927b6e01e | ||
|
|
987a7657ce | ||
|
|
94a78b3122 | ||
|
|
fcc458bf31 | ||
|
|
14987c1170 | ||
|
|
46b0ceede3 | ||
|
|
4bbb447e74 | ||
|
|
ebbbbb04ab | ||
|
|
54bad805c1 | ||
|
|
cecade9845 | ||
|
|
f61a08fe50 | ||
|
|
573fbb8fb1 | ||
|
|
e99c07192d | ||
|
|
9252465cab | ||
|
|
a778eb1cc9 | ||
|
|
2bf2052248 | ||
|
|
d903336fe0 | ||
|
|
b7dcff0bbc | ||
|
|
3e6ceb4bb9 | ||
|
|
b2767947a0 | ||
|
|
a24258834e | ||
|
|
76070234d4 | ||
|
|
d2ea17ec30 | ||
|
|
c0e05be791 | ||
|
|
12a2dee175 | ||
|
|
ac3dcdd0d0 | ||
|
|
4257f6a199 | ||
|
|
5fd9fc5e26 | ||
|
|
adbc95c5d5 | ||
|
|
99a1e4aa74 | ||
|
|
2ae82d71d6 | ||
|
|
8a2faf2955 | ||
|
|
1d41f4b138 | ||
|
|
e1c96655b1 | ||
|
|
68fe4a1dc1 | ||
|
|
adf15b7bd3 | ||
|
|
e77ac9617d | ||
|
|
54824879c5 | ||
|
|
bf8580248b | ||
|
|
e1978541db | ||
|
|
aa7580aa5a | ||
|
|
4366cd8c81 | ||
|
|
2a5242a4d9 | ||
|
|
a4dc545a4f | ||
|
|
f16ce5e4a4 | ||
|
|
03cdf67439 | ||
|
|
e8e86dcc92 | ||
|
|
a3e3b0d8c6 | ||
|
|
23f31b1639 | ||
|
|
8d164340c6 | ||
|
|
f39f90728f |
@@ -1,2 +0,0 @@
|
|||||||
downloads
|
|
||||||
.git
|
|
||||||
9
.gitignore
vendored
9
.gitignore
vendored
@@ -1,3 +1,4 @@
|
|||||||
|
.dockerignore
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.vscode
|
.vscode
|
||||||
test/docker/coverage
|
test/docker/coverage
|
||||||
@@ -5,7 +6,7 @@ test/docker/vendor
|
|||||||
test/kubernetes/vendor
|
test/kubernetes/vendor
|
||||||
build
|
build
|
||||||
coverage
|
coverage
|
||||||
downloads
|
#downloads
|
||||||
incubating/mqipt/ms81*
|
incubating/mqipt/ms81*
|
||||||
vendor/github.com/prometheus/client_model/bin/
|
vendor/github.com/prometheus/client_model/bin/
|
||||||
vendor/github.com/prometheus/client_model/.classpath
|
vendor/github.com/prometheus/client_model/.classpath
|
||||||
@@ -14,3 +15,9 @@ vendor/github.com/prometheus/client_model/.settings*
|
|||||||
gosec_results.json
|
gosec_results.json
|
||||||
internal/qmgrauth/qmgroam/patch
|
internal/qmgrauth/qmgroam/patch
|
||||||
.tagcache
|
.tagcache
|
||||||
|
|
||||||
|
# Nix
|
||||||
|
*.nix
|
||||||
|
.envrc
|
||||||
|
.direnv
|
||||||
|
result
|
||||||
|
|||||||
77
.travis.yml
77
.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");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
@@ -12,13 +12,13 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
dist: xenial
|
dist: bionic
|
||||||
|
group: beta
|
||||||
sudo: required
|
sudo: required
|
||||||
language: go
|
language: go
|
||||||
group: xeniallegacy
|
|
||||||
|
|
||||||
go:
|
go:
|
||||||
- "1.13.15"
|
- "1.19.9"
|
||||||
|
|
||||||
services:
|
services:
|
||||||
- docker
|
- docker
|
||||||
@@ -26,10 +26,8 @@ services:
|
|||||||
env:
|
env:
|
||||||
global:
|
global:
|
||||||
- MAIN_BRANCH=private-master
|
- MAIN_BRANCH=private-master
|
||||||
- MQ_LTS_VERSION=9.2.0.1
|
|
||||||
- TAGCACHE_FILE=tagcache
|
- TAGCACHE_FILE=tagcache
|
||||||
- RELEASE=r2
|
- RELEASE=r1
|
||||||
- RELEASE_LTS=r1
|
|
||||||
|
|
||||||
go_import_path: "github.com/ibm-messaging/mq-container"
|
go_import_path: "github.com/ibm-messaging/mq-container"
|
||||||
|
|
||||||
@@ -44,7 +42,7 @@ jobs:
|
|||||||
name: "Basic AMD64 build"
|
name: "Basic AMD64 build"
|
||||||
os: linux
|
os: linux
|
||||||
env:
|
env:
|
||||||
- MQ_ARCHIVE_REPOSITORY_DEV=$MQ_920_ARCHIVE_REPOSITORY_DEV_AMD64
|
- MQ_ARCHIVE_REPOSITORY_DEV=$MQ_933_ARCHIVE_REPOSITORY_DEV_AMD64
|
||||||
script: bash -e travis-build-scripts/run.sh
|
script: bash -e travis-build-scripts/run.sh
|
||||||
|
|
||||||
# CD Build
|
# CD Build
|
||||||
@@ -60,18 +58,9 @@ jobs:
|
|||||||
os: linux
|
os: linux
|
||||||
env:
|
env:
|
||||||
- BUILD_ALL=true
|
- BUILD_ALL=true
|
||||||
- MQ_ARCHIVE_REPOSITORY=$MQ_920_ARCHIVE_REPOSITORY_AMD64
|
- MQ_ARCHIVE_REPOSITORY=$MQ_933_ARCHIVE_REPOSITORY_AMD64
|
||||||
- MQ_ARCHIVE_REPOSITORY_DEV=$MQ_920_ARCHIVE_REPOSITORY_DEV_AMD64
|
- MQ_ARCHIVE_REPOSITORY_DEV=$MQ_933_ARCHIVE_REPOSITORY_DEV_AMD64
|
||||||
script: bash -e travis-build-scripts/run.sh
|
script: bash -e travis-build-scripts/run.sh
|
||||||
# - 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_920_ARCHIVE_REPOSITORY_PPC64LE
|
|
||||||
# - MQ_ARCHIVE_REPOSITORY_DEV=$MQ_920_ARCHIVE_REPOSITORY_DEV_PPC64LE
|
|
||||||
# script: bash -e travis-build-scripts/run.sh
|
|
||||||
- stage: build
|
- stage: build
|
||||||
if: branch = private-master OR tag =~ ^release-candidate*
|
if: branch = private-master OR tag =~ ^release-candidate*
|
||||||
name: "Multi-Arch S390X build"
|
name: "Multi-Arch S390X build"
|
||||||
@@ -79,60 +68,24 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
- BUILD_ALL=true
|
- BUILD_ALL=true
|
||||||
- TEST_OPTS_DOCKER="-run TestGoldenPathWithMetrics"
|
- TEST_OPTS_DOCKER="-run TestGoldenPathWithMetrics"
|
||||||
- MQ_ARCHIVE_REPOSITORY=$MQ_920_ARCHIVE_REPOSITORY_S390X
|
- MQ_ARCHIVE_REPOSITORY=$MQ_933_ARCHIVE_REPOSITORY_S390X
|
||||||
- MQ_ARCHIVE_REPOSITORY_DEV=$MQ_920_ARCHIVE_REPOSITORY_DEV_S390X
|
- MQ_ARCHIVE_REPOSITORY_DEV=$MQ_933_ARCHIVE_REPOSITORY_DEV_S390X
|
||||||
script: bash -e travis-build-scripts/run.sh
|
|
||||||
- stage: push-manifest
|
|
||||||
if: branch = private-master AND type != pull_request OR tag =~ ^release-candidate*
|
|
||||||
name: "Push Manifest-list to registry"
|
|
||||||
env:
|
|
||||||
- PUSH_MANIFEST_ONLY=true
|
|
||||||
script: bash -e travis-build-scripts/run.sh
|
|
||||||
|
|
||||||
# LTS Build
|
|
||||||
|
|
||||||
- stage: global-tag
|
|
||||||
if: branch = private-master AND type != pull_request OR tag =~ ^release-candidate*
|
|
||||||
name: "Generate Global Tag"
|
|
||||||
os: linux
|
|
||||||
env:
|
|
||||||
- LTS=true
|
|
||||||
- TAGCACHE_FILE=tagcache-lts
|
|
||||||
- MQ_VERSION=$MQ_LTS_VERSION
|
|
||||||
- RELEASE=$RELEASE_LTS
|
|
||||||
script: bash -e travis-build-scripts/global-tag.sh
|
|
||||||
- stage: build
|
|
||||||
if: branch = private-master OR tag =~ ^release-candidate*
|
|
||||||
name: "Multi-Arch AMD64 build"
|
|
||||||
os: linux
|
|
||||||
env:
|
|
||||||
- LTS=true
|
|
||||||
- TAGCACHE_FILE=tagcache-lts
|
|
||||||
- MQ_VERSION=$MQ_LTS_VERSION
|
|
||||||
- MQ_ARCHIVE_REPOSITORY=$MQ_9201_EUS_ARCHIVE_REPOSITORY_AMD64
|
|
||||||
- RELEASE=$RELEASE_LTS
|
|
||||||
script: bash -e travis-build-scripts/run.sh
|
script: bash -e travis-build-scripts/run.sh
|
||||||
- stage: build
|
- stage: build
|
||||||
if: branch = private-master OR tag =~ ^release-candidate*
|
if: branch = private-master OR tag =~ ^release-candidate*
|
||||||
name: "Multi-Arch S390X build"
|
name: "Multi-Arch PPC64LE build"
|
||||||
os: linux-s390
|
os: linux-ppc64le
|
||||||
env:
|
env:
|
||||||
- LTS=true
|
- BUILD_ALL=true
|
||||||
- TAGCACHE_FILE=tagcache-lts
|
|
||||||
- MQ_VERSION=$MQ_LTS_VERSION
|
|
||||||
- TEST_OPTS_DOCKER="-run TestGoldenPathWithMetrics"
|
- TEST_OPTS_DOCKER="-run TestGoldenPathWithMetrics"
|
||||||
- MQ_ARCHIVE_REPOSITORY=$MQ_9201_EUS_ARCHIVE_REPOSITORY_S390X
|
- MQ_ARCHIVE_REPOSITORY=$MQ_933_ARCHIVE_REPOSITORY_PPC64LE
|
||||||
- RELEASE=$RELEASE_LTS
|
- MQ_ARCHIVE_REPOSITORY_DEV=$MQ_933_ARCHIVE_REPOSITORY_DEV_PPC64LE
|
||||||
script: bash -e travis-build-scripts/run.sh
|
script: bash -e travis-build-scripts/run.sh
|
||||||
- stage: push-manifest
|
- stage: push-manifest
|
||||||
if: branch = private-master 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"
|
name: "Push Manifest-list to registry"
|
||||||
env:
|
env:
|
||||||
- LTS=true
|
|
||||||
- TAGCACHE_FILE=tagcache-lts
|
|
||||||
- MQ_VERSION=$MQ_LTS_VERSION
|
|
||||||
- PUSH_MANIFEST_ONLY=true
|
- PUSH_MANIFEST_ONLY=true
|
||||||
- RELEASE=$RELEASE_LTS
|
|
||||||
script: bash -e travis-build-scripts/run.sh
|
script: bash -e travis-build-scripts/run.sh
|
||||||
|
|
||||||
before_install:
|
before_install:
|
||||||
|
|||||||
10
.whitesource
Normal file
10
.whitesource
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"settingsInheritedFrom": "whitesource-config/whitesource-config@master",
|
||||||
|
"scanSettings": {
|
||||||
|
"configMode": "LOCAL",
|
||||||
|
"baseBranches": ["private-master", "v9.2.0.x-eus", "v9.3.0.x"]
|
||||||
|
},
|
||||||
|
"issueSettings": {
|
||||||
|
"issueRepoName": "whitesource-scan-issues"
|
||||||
|
}
|
||||||
|
}
|
||||||
39
CBO.md
Normal file
39
CBO.md
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
# MQ in Docker
|
||||||
|
|
||||||
|
## Download new version
|
||||||
|
Download IBM MQ Advanced for Developers here: https://public.dhe.ibm.com/ibmdl/export/pub/software/websphere/messaging/mqadv/
|
||||||
|
Place the file in the downloads folder of this repo.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir -p downloads
|
||||||
|
wget -P downloads https://public.dhe.ibm.com/ibmdl/export/pub/software/websphere/messaging/mqadv/9.3.3.1-IBM-MQ-Advanced-for-Developers-Non-Install-LinuxX64.tar.gz
|
||||||
|
wget -P downloads https://public.dhe.ibm.com/ibmdl/export/pub/software/websphere/messaging/mqadv/9.3.3.1-IBM-MQ-Advanced-for-Developers-Non-Install-LinuxARM64.tar.gz
|
||||||
|
```
|
||||||
|
|
||||||
|
# Build Docker image
|
||||||
|
Update ```MQ_ARCHIVE``` with the new filename in the two Dockerfiles
|
||||||
|
```bash
|
||||||
|
nano Dockerfile-server-arm
|
||||||
|
nano Dockerfile-server-x64
|
||||||
|
```
|
||||||
|
|
||||||
|
Build the new images
|
||||||
|
```bash
|
||||||
|
docker buildx build -t <tag> -f <dockerfile> .
|
||||||
|
|
||||||
|
# Build for various architechtures
|
||||||
|
docker buildx build --platform linux/amd64 -t git.cbo.dk/academy/mq:9.3.3.1-amd64 -f Dockerfile-server-x64 .
|
||||||
|
docker buildx build --platform linux/arm64 -t git.cbo.dk/academy/mq:9.3.3.1-arm64 -f Dockerfile-server-arm .
|
||||||
|
docker image tag git.cbo.dk/academy/mq:9.3.3.1-amd64 git.cbo.dk/academy/mq:latest
|
||||||
|
|
||||||
|
# Push to registry (Optional)
|
||||||
|
docker login git.cbo.dk
|
||||||
|
docker push --all-tags git.cbo.dk/academy/mq
|
||||||
|
```
|
||||||
|
|
||||||
|
# Deploy MQ
|
||||||
|
```bash
|
||||||
|
docker stop ibmmq
|
||||||
|
docker rm ibmmq
|
||||||
|
docker run --name "ibmmq" -d -p 1414:1414 -p 9157:9157 -p 9443:9443 -e LICENSE=accept -e MQ_ADMIN_PASSWORD=passw0rd -e MQ_QMGR_NAME=MQDOCKER -e MQ_ENABLE_METRICS=true --name ibmmq git.cbo.dk/academy/mq:latest
|
||||||
|
```
|
||||||
50
CHANGELOG.md
50
CHANGELOG.md
@@ -1,5 +1,55 @@
|
|||||||
# Change log
|
# Change log
|
||||||
|
|
||||||
|
## 9.3.3.0 (2023-06)
|
||||||
|
|
||||||
|
* Updated to MQ version 9.3.3.0
|
||||||
|
|
||||||
|
## 9.3.2.0 (2023-02)
|
||||||
|
|
||||||
|
* 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)
|
||||||
|
|
||||||
|
* Queue manager attribute SSLKEYR is now set to blank instead of '/run/runmqserver/tls/key' if key and certificate are not supplied.
|
||||||
|
|
||||||
|
## 9.3.1.0 (2022-10)
|
||||||
|
|
||||||
|
* Updated to MQ version 9.3.1.0
|
||||||
|
|
||||||
|
## 9.3.0.0 (2022-06)
|
||||||
|
|
||||||
|
* Updated to MQ version 9.3.0.0
|
||||||
|
* Use `registry.access.redhat.com` instead of `registry.redhat.io`, so that you don't need to login with a Red Hat account.
|
||||||
|
* Updated default developer config to use TLS cipher `ANY_TLS12_OR_HIGHER` instead of `ANY_TLS12`
|
||||||
|
* Added default `jvm.options` file fix issue with missing preferences file causing an error in the web server log.
|
||||||
|
* Updated to allow building image from Podman on macOS (requires Podman 4.1)
|
||||||
|
* Container builds are now faster
|
||||||
|
* Updated signal handling to use a buffer, as recommended by the Go 1.17 vetting tool
|
||||||
|
|
||||||
|
## 9.2.5.0 (2022-03)
|
||||||
|
|
||||||
|
* Updated to MQ version 9.2.5.0
|
||||||
|
|
||||||
|
## 9.2.4.0 (2021-11)
|
||||||
|
|
||||||
|
* Updated to MQ version 9.2.4.0
|
||||||
|
|
||||||
|
## 9.2.3.0 (2021-07-22)
|
||||||
|
|
||||||
|
* Updated to MQ version 9.2.3.0
|
||||||
|
|
||||||
|
## 9.2.2.0 (2021-03-26)
|
||||||
|
|
||||||
|
* Updated to MQ version 9.2.2.0
|
||||||
|
|
||||||
|
## 9.2.1.0 (2020-02-18)
|
||||||
|
|
||||||
|
* Updated to MQ version 9.2.1.0
|
||||||
|
|
||||||
|
|
||||||
## 9.2.0.1-LTS (2020-12-04)
|
## 9.2.0.1-LTS (2020-12-04)
|
||||||
|
|
||||||
* Added support for MQ Long Term Support (production licensed only) in the mq-container
|
* Added support for MQ Long Term Support (production licensed only) in the mq-container
|
||||||
|
|||||||
@@ -1,164 +0,0 @@
|
|||||||
# © Copyright IBM Corporation 2015, 2020
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
|
|
||||||
ARG BASE_IMAGE=registry.redhat.io/ubi8/ubi-minimal
|
|
||||||
ARG BASE_TAG=8.3-201
|
|
||||||
ARG GO_WORKDIR=/go/src/github.com/ibm-messaging/mq-container
|
|
||||||
ARG MQ_URL="https://public.dhe.ibm.com/ibmdl/export/pub/software/websphere/messaging/mqadv/9.2.0.0-IBM-MQ-Advanced-for-Developers-Non-Install-LinuxX64.tar.gz"
|
|
||||||
###############################################################################
|
|
||||||
# Build stage to build Go code
|
|
||||||
###############################################################################
|
|
||||||
FROM docker.io/golang:1.14.12 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
|
|
||||||
USER 0
|
|
||||||
COPY install-mq.sh /usr/local/bin/
|
|
||||||
RUN mkdir /opt/mqm \
|
|
||||||
&& chmod a+x /usr/local/bin/install-mq.sh \
|
|
||||||
&& sleep 1 \
|
|
||||||
&& INSTALL_SDK=1 install-mq.sh \
|
|
||||||
&& chown -R 1001:root /opt/mqm/*
|
|
||||||
WORKDIR $GO_WORKDIR/
|
|
||||||
COPY cmd/ ./cmd
|
|
||||||
COPY internal/ ./internal
|
|
||||||
COPY pkg/ ./pkg
|
|
||||||
COPY vendor/ ./vendor
|
|
||||||
ENV CGO_CFLAGS="-I/opt/mqm/inc/" \
|
|
||||||
CGO_LDFLAGS_ALLOW="-Wl,-rpath.*"
|
|
||||||
ENV 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/
|
|
||||||
RUN go build ./cmd/chkmqready/
|
|
||||||
RUN go build ./cmd/chkmqhealthy/
|
|
||||||
RUN go build ./cmd/runmqdevserver/
|
|
||||||
RUN go build -buildmode=c-shared -o amqpasdev.so ./internal/qmgrauth/pas.go
|
|
||||||
RUN go test -v ./cmd/runmqdevserver/...
|
|
||||||
RUN go test -v ./cmd/runmqserver/
|
|
||||||
RUN go test -v ./cmd/chkmqready/
|
|
||||||
RUN go test -v ./cmd/chkmqhealthy/
|
|
||||||
RUN go test -v ./pkg/...
|
|
||||||
RUN go test -v ./internal/...
|
|
||||||
RUN go vet ./cmd/... ./internal/...
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# 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
|
|
||||||
ARG GO_WORKDIR
|
|
||||||
LABEL summary="IBM MQ Advanced Server"
|
|
||||||
LABEL description="Simplify, accelerate and facilitate the reliable exchange of data with a security-rich messaging solution — trusted by the world’s most successful enterprises"
|
|
||||||
LABEL vendor="IBM"
|
|
||||||
LABEL maintainer="IBM"
|
|
||||||
LABEL distribution-scope="private"
|
|
||||||
LABEL authoritative-source-url="https://www.ibm.com/software/passportadvantage/"
|
|
||||||
LABEL url="https://www.ibm.com/products/mq/advanced"
|
|
||||||
LABEL io.openshift.tags="mq messaging"
|
|
||||||
LABEL io.k8s.display-name="IBM MQ Advanced Server"
|
|
||||||
LABEL io.k8s.description="Simplify, accelerate and facilitate the reliable exchange of data with a security-rich messaging solution — trusted by the world’s most successful enterprises"
|
|
||||||
LABEL base-image=$BASE_IMAGE
|
|
||||||
LABEL base-image-release=$BASE_TAG
|
|
||||||
COPY install-mq.sh /usr/local/bin/
|
|
||||||
COPY install-mq-server-prereqs.sh /usr/local/bin/
|
|
||||||
# Install MQ. To avoid a "text file busy" error here, we sleep before installing.
|
|
||||||
RUN env \
|
|
||||||
&& mkdir /opt/mqm \
|
|
||||||
&& chmod u+x /usr/local/bin/install-*.sh \
|
|
||||||
&& sleep 1 \
|
|
||||||
&& install-mq-server-prereqs.sh \
|
|
||||||
&& install-mq.sh \
|
|
||||||
&& /opt/mqm/bin/security/amqpamcf \
|
|
||||||
&& chown -R 1001:root /opt/mqm/*
|
|
||||||
# Create a directory for runtime data from runmqserver
|
|
||||||
RUN mkdir -p /run/runmqserver \
|
|
||||||
&& chown 1001:root /run/runmqserver
|
|
||||||
COPY --from=builder $GO_WORKDIR/runmqserver /usr/local/bin/
|
|
||||||
COPY --from=builder $GO_WORKDIR/chkmq* /usr/local/bin/
|
|
||||||
COPY NOTICES.txt /opt/mqm/licenses/notices-container.txt
|
|
||||||
# Copy web XML files
|
|
||||||
COPY web /etc/mqm/web
|
|
||||||
COPY etc/mqm/*.tpl /etc/mqm/
|
|
||||||
RUN chmod ug+x /usr/local/bin/runmqserver \
|
|
||||||
&& chown 1001:root /usr/local/bin/*mq* \
|
|
||||||
&& chmod ug+x /usr/local/bin/chkmq* \
|
|
||||||
&& chown -R 1001:root /etc/mqm/* \
|
|
||||||
&& install --directory --mode 2775 --owner 1001 --group root /run/runmqserver \
|
|
||||||
&& touch /run/termination-log \
|
|
||||||
&& chown 1001:root /run/termination-log \
|
|
||||||
&& chmod 0660 /run/termination-log \
|
|
||||||
&& chmod -R g+w /etc/mqm/web
|
|
||||||
# Always use port 1414 for MQ & 9157 for the metrics
|
|
||||||
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
|
|
||||||
# We can run as any UID
|
|
||||||
USER 1001
|
|
||||||
ENV MQ_CONNAUTH_USE_HTP=false
|
|
||||||
ENTRYPOINT ["runmqserver"]
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# Add default developer config
|
|
||||||
###############################################################################
|
|
||||||
FROM mq-server AS mq-dev-server
|
|
||||||
ARG BASE_IMAGE
|
|
||||||
ARG BASE_TAG
|
|
||||||
ARG GO_WORKDIR
|
|
||||||
# Enable MQ developer default configuration
|
|
||||||
ENV MQ_DEV=true
|
|
||||||
LABEL summary="IBM MQ Advanced for Developers Server"
|
|
||||||
LABEL description="Simplify, accelerate and facilitate the reliable exchange of data with a security-rich messaging solution — trusted by the world’s most successful enterprises"
|
|
||||||
LABEL vendor="IBM"
|
|
||||||
LABEL distribution-scope="private"
|
|
||||||
LABEL authoritative-source-url="https://www.ibm.com/software/passportadvantage/"
|
|
||||||
LABEL url="https://www.ibm.com/products/mq/advanced"
|
|
||||||
LABEL io.openshift.tags="mq messaging"
|
|
||||||
LABEL io.k8s.display-name="IBM MQ Advanced for Developers Server"
|
|
||||||
LABEL io.k8s.description="Simplify, accelerate and facilitate the reliable exchange of data with a security-rich messaging solution — trusted by the world’s most successful enterprises"
|
|
||||||
LABEL base-image=$BASE_IMAGE
|
|
||||||
LABEL base-image-release=$BASE_TAG
|
|
||||||
USER 0
|
|
||||||
COPY --from=builder $GO_WORKDIR/amqpas* /opt/mqm/lib64/
|
|
||||||
COPY etc/mqm/*.ini /etc/mqm/
|
|
||||||
COPY etc/mqm/mq.htpasswd /etc/mqm/
|
|
||||||
RUN chmod 0660 /etc/mqm/mq.htpasswd
|
|
||||||
COPY incubating/mqadvanced-server-dev/install-extra-packages.sh /usr/local/bin/
|
|
||||||
RUN chmod u+x /usr/local/bin/install-extra-packages.sh \
|
|
||||||
&& sleep 1 \
|
|
||||||
&& install-extra-packages.sh
|
|
||||||
# Create a directory for runtime data from runmqserver
|
|
||||||
RUN mkdir -p /run/runmqdevserver \
|
|
||||||
&& chown 1001:root /run/runmqdevserver
|
|
||||||
COPY --from=builder $GO_WORKDIR/runmqdevserver /usr/local/bin/
|
|
||||||
# Copy template files
|
|
||||||
COPY incubating/mqadvanced-server-dev/*.tpl /etc/mqm/
|
|
||||||
# Copy web XML files for default developer configuration
|
|
||||||
COPY incubating/mqadvanced-server-dev/web /etc/mqm/web
|
|
||||||
RUN chown -R 1001:root /etc/mqm/* \
|
|
||||||
&& chmod -R g+w /etc/mqm/web \
|
|
||||||
&& chmod +x /usr/local/bin/runmq* \
|
|
||||||
&& install --directory --mode 2775 --owner 1001 --group root /run/runmqdevserver
|
|
||||||
ENV MQ_ENABLE_EMBEDDED_WEB_SERVER=1 MQ_GENERATE_CERTIFICATE_HOSTNAME=localhost
|
|
||||||
ENV LD_LIBRARY_PATH=/opt/mqm/lib64
|
|
||||||
ENV MQS_PERMIT_UNKNOWN_ID=true
|
|
||||||
ENV MQ_CONNAUTH_USE_HTP=true
|
|
||||||
USER 1001
|
|
||||||
ENTRYPOINT ["runmqdevserver"]
|
|
||||||
210
Dockerfile-server-arm
Normal file
210
Dockerfile-server-arm
Normal file
@@ -0,0 +1,210 @@
|
|||||||
|
# © 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.
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
# Download IBM MQ Advanced for Developers here: https://public.dhe.ibm.com/ibmdl/export/pub/software/websphere/messaging/mqadv/
|
||||||
|
# mkdir downloads
|
||||||
|
# Kopier 9.3.3.0-IBM-MQ-Advanced-for-Developers-Non-Install-LinuxARM64.tar.gz ind i downloads.
|
||||||
|
# Opdater .dockerignore
|
||||||
|
# docker build -t mqserver -f Dockerfile-server-arm .
|
||||||
|
# docker run -d -p 1414:1414 -p 9157:9157 -p 9443:9443 -e LICENSE=accept --name mqserver mqserver
|
||||||
|
|
||||||
|
ARG BASE_IMAGE=registry.access.redhat.com/ubi8/ubi-minimal
|
||||||
|
ARG BASE_TAG=8.10-896.1716497715
|
||||||
|
ARG BUILDER_IMAGE=registry.access.redhat.com/ubi8/go-toolset
|
||||||
|
ARG BUILDER_TAG=1.21.9-3.1716505664
|
||||||
|
ARG GO_WORKDIR=/opt/app-root/src/go/src/github.com/ibm-messaging/mq-container
|
||||||
|
ARG MQ_ARCHIVE="downloads/9.3.3.1-IBM-MQ-Advanced-for-Developers-Non-Install-LinuxARM64.tar.gz"
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Build stage to build Go code
|
||||||
|
###############################################################################
|
||||||
|
FROM $BUILDER_IMAGE:$BUILDER_TAG as builder
|
||||||
|
ARG IMAGE_REVISION="Not specified"
|
||||||
|
ARG IMAGE_SOURCE="Not specified"
|
||||||
|
ARG IMAGE_TAG="Not specified"
|
||||||
|
ARG GO_WORKDIR
|
||||||
|
ARG MQ_ARCHIVE
|
||||||
|
USER 0
|
||||||
|
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
|
||||||
|
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/ \
|
||||||
|
&& go build ./cmd/chkmqstarted/ \
|
||||||
|
&& go build ./cmd/runmqdevserver/ \
|
||||||
|
&& go test -v ./cmd/runmqdevserver/... \
|
||||||
|
&& go test -v ./cmd/runmqserver/ \
|
||||||
|
&& go test -v ./cmd/chkmqready/ \
|
||||||
|
&& go test -v ./cmd/chkmqhealthy/ \
|
||||||
|
&& go test -v ./cmd/chkmqstarted/ \
|
||||||
|
&& go test -v ./pkg/... \
|
||||||
|
&& 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
|
||||||
|
ARG MQ_URL
|
||||||
|
ARG BASE_IMAGE
|
||||||
|
ARG BASE_TAG
|
||||||
|
ARG GO_WORKDIR
|
||||||
|
LABEL summary="IBM MQ Advanced Server" \
|
||||||
|
description="Simplify, accelerate and facilitate the reliable exchange of data with a security-rich messaging solution — trusted by the world’s most successful enterprises" \
|
||||||
|
vendor="IBM" \
|
||||||
|
maintainer="IBM" \
|
||||||
|
distribution-scope="private" \
|
||||||
|
authoritative-source-url="https://www.ibm.com/software/passportadvantage/" \
|
||||||
|
url="https://www.ibm.com/products/mq/advanced" \
|
||||||
|
io.openshift.tags="mq messaging" \
|
||||||
|
io.k8s.display-name="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 --from=mq-redux /opt/mqm-redux/ /opt/mqm/
|
||||||
|
COPY setup-image.sh /usr/local/bin/
|
||||||
|
COPY install-mq-server-prereqs.sh /usr/local/bin/
|
||||||
|
RUN env \
|
||||||
|
&& chmod u+x /usr/local/bin/install-*.sh \
|
||||||
|
&& chmod u+x /usr/local/bin/setup-image.sh \
|
||||||
|
&& install-mq-server-prereqs.sh \
|
||||||
|
&& setup-image.sh \
|
||||||
|
&& /opt/mqm/bin/security/amqpamcf \
|
||||||
|
&& chown -R 1001:root /opt/mqm/*
|
||||||
|
COPY --from=builder $GO_WORKDIR/runmqserver /usr/local/bin/
|
||||||
|
COPY --from=builder $GO_WORKDIR/chkmq* /usr/local/bin/
|
||||||
|
COPY NOTICES.txt /opt/mqm/licenses/notices-container.txt
|
||||||
|
COPY ha/native-ha.ini.tpl /etc/mqm/native-ha.ini.tpl
|
||||||
|
# Copy web XML files
|
||||||
|
COPY web /etc/mqm/web
|
||||||
|
COPY etc/mqm/*.tpl /etc/mqm/
|
||||||
|
RUN chmod ug+x /usr/local/bin/runmqserver \
|
||||||
|
&& chown 1001:root /usr/local/bin/*mq* \
|
||||||
|
&& chmod ug+x /usr/local/bin/chkmq* \
|
||||||
|
&& chown -R 1001:root /etc/mqm/* \
|
||||||
|
&& install --directory --mode 2775 --owner 1001 --group root /run/runmqserver \
|
||||||
|
&& touch /run/termination-log \
|
||||||
|
&& chown 1001:root /run/termination-log \
|
||||||
|
&& chmod 0660 /run/termination-log \
|
||||||
|
&& chmod -R g+w /etc/mqm/web
|
||||||
|
# Always use port 1414 for MQ & 9157 for the metrics
|
||||||
|
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
|
||||||
|
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
|
||||||
|
ENTRYPOINT ["runmqserver"]
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Build stage to build C code for custom authorization service (developer-only)
|
||||||
|
###############################################################################
|
||||||
|
# Use the Go toolset image, which already includes gcc and the MQ SDK
|
||||||
|
FROM builder as cbuilder
|
||||||
|
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
|
||||||
|
COPY authservice/ /opt/app-root/src/authservice/
|
||||||
|
WORKDIR /opt/app-root/src/authservice/mqhtpass
|
||||||
|
RUN make all
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Add default developer config
|
||||||
|
###############################################################################
|
||||||
|
FROM mq-server AS mq-dev-server
|
||||||
|
ARG BASE_IMAGE
|
||||||
|
ARG BASE_TAG
|
||||||
|
ARG GO_WORKDIR
|
||||||
|
LABEL summary="IBM MQ Advanced for Developers Server" \
|
||||||
|
description="Simplify, accelerate and facilitate the reliable exchange of data with a security-rich messaging solution — trusted by the world’s most successful enterprises" \
|
||||||
|
vendor="IBM" \
|
||||||
|
distribution-scope="private" \
|
||||||
|
authoritative-source-url="https://www.ibm.com/software/passportadvantage/" \
|
||||||
|
url="https://www.ibm.com/products/mq/advanced" \
|
||||||
|
io.openshift.tags="mq messaging" \
|
||||||
|
io.k8s.display-name="IBM MQ Advanced for Developers 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
|
||||||
|
USER 0
|
||||||
|
COPY --from=cbuilder /opt/app-root/src/authservice/mqhtpass/build/mqhtpass.so /opt/mqm/lib64/
|
||||||
|
COPY etc/mqm/*.ini /etc/mqm/
|
||||||
|
COPY etc/mqm/mq.htpasswd /etc/mqm/
|
||||||
|
COPY incubating/mqadvanced-server-dev/install-extra-packages.sh /usr/local/bin/
|
||||||
|
RUN chmod u+x /usr/local/bin/install-extra-packages.sh \
|
||||||
|
&& sleep 1 \
|
||||||
|
&& install-extra-packages.sh
|
||||||
|
COPY --from=builder $GO_WORKDIR/runmqdevserver /usr/local/bin/
|
||||||
|
# Copy template files
|
||||||
|
COPY incubating/mqadvanced-server-dev/*.tpl /etc/mqm/
|
||||||
|
# Copy web XML files for default developer configuration
|
||||||
|
COPY incubating/mqadvanced-server-dev/web /etc/mqm/web
|
||||||
|
RUN chown -R 1001:root /etc/mqm/* \
|
||||||
|
&& chmod -R g+w /etc/mqm/web \
|
||||||
|
&& chmod +x /usr/local/bin/runmq* \
|
||||||
|
&& chmod 0660 /etc/mqm/mq.htpasswd \
|
||||||
|
&& install --directory --mode 2775 --owner 1001 --group root /run/runmqdevserver
|
||||||
|
ENV MQ_DEV=true \
|
||||||
|
MQ_ENABLE_EMBEDDED_WEB_SERVER=1 \
|
||||||
|
MQ_GENERATE_CERTIFICATE_HOSTNAME=localhost \
|
||||||
|
LD_LIBRARY_PATH=/opt/mqm/lib64 \
|
||||||
|
MQ_CONNAUTH_USE_HTP=true \
|
||||||
|
MQS_PERMIT_UNKNOWN_ID=true
|
||||||
|
USER 1001
|
||||||
|
ENTRYPOINT ["runmqdevserver"]
|
||||||
210
Dockerfile-server-x64
Normal file
210
Dockerfile-server-x64
Normal file
@@ -0,0 +1,210 @@
|
|||||||
|
# © 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.
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
# Download IBM MQ Advanced for Developers here: https://public.dhe.ibm.com/ibmdl/export/pub/software/websphere/messaging/mqadv/
|
||||||
|
# mkdir downloads
|
||||||
|
# Kopier 9.3.3.0-IBM-MQ-Advanced-for-Developers-Non-Install-LinuxX64.tar.gz ind i downloads.
|
||||||
|
# Opdater .dockerignore
|
||||||
|
# docker build -t mqserver -f Dockerfile-server-x64 .
|
||||||
|
# docker run -d -p 1414:1414 -p 9157:9157 -p 9443:9443 -e LICENSE=accept --name mqserver mqserver
|
||||||
|
|
||||||
|
ARG BASE_IMAGE=registry.access.redhat.com/ubi8/ubi-minimal
|
||||||
|
ARG BASE_TAG=8.10-896.1716497715
|
||||||
|
ARG BUILDER_IMAGE=registry.access.redhat.com/ubi8/go-toolset
|
||||||
|
ARG BUILDER_TAG=1.21.9-3.1716505664
|
||||||
|
ARG GO_WORKDIR=/opt/app-root/src/go/src/github.com/ibm-messaging/mq-container
|
||||||
|
ARG MQ_ARCHIVE="downloads/9.3.3.0-IBM-MQ-Advanced-for-Developers-Non-Install-LinuxX64.tar.gz"
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Build stage to build Go code
|
||||||
|
###############################################################################
|
||||||
|
FROM $BUILDER_IMAGE:$BUILDER_TAG as builder
|
||||||
|
ARG IMAGE_REVISION="Not specified"
|
||||||
|
ARG IMAGE_SOURCE="Not specified"
|
||||||
|
ARG IMAGE_TAG="Not specified"
|
||||||
|
ARG GO_WORKDIR
|
||||||
|
ARG MQ_ARCHIVE
|
||||||
|
USER 0
|
||||||
|
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
|
||||||
|
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/ \
|
||||||
|
&& go build ./cmd/chkmqstarted/ \
|
||||||
|
&& go build ./cmd/runmqdevserver/ \
|
||||||
|
&& go test -v ./cmd/runmqdevserver/... \
|
||||||
|
&& go test -v ./cmd/runmqserver/ \
|
||||||
|
&& go test -v ./cmd/chkmqready/ \
|
||||||
|
&& go test -v ./cmd/chkmqhealthy/ \
|
||||||
|
&& go test -v ./cmd/chkmqstarted/ \
|
||||||
|
&& go test -v ./pkg/... \
|
||||||
|
&& 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
|
||||||
|
ARG MQ_URL
|
||||||
|
ARG BASE_IMAGE
|
||||||
|
ARG BASE_TAG
|
||||||
|
ARG GO_WORKDIR
|
||||||
|
LABEL summary="IBM MQ Advanced Server" \
|
||||||
|
description="Simplify, accelerate and facilitate the reliable exchange of data with a security-rich messaging solution — trusted by the world’s most successful enterprises" \
|
||||||
|
vendor="IBM" \
|
||||||
|
maintainer="IBM" \
|
||||||
|
distribution-scope="private" \
|
||||||
|
authoritative-source-url="https://www.ibm.com/software/passportadvantage/" \
|
||||||
|
url="https://www.ibm.com/products/mq/advanced" \
|
||||||
|
io.openshift.tags="mq messaging" \
|
||||||
|
io.k8s.display-name="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 --from=mq-redux /opt/mqm-redux/ /opt/mqm/
|
||||||
|
COPY setup-image.sh /usr/local/bin/
|
||||||
|
COPY install-mq-server-prereqs.sh /usr/local/bin/
|
||||||
|
RUN env \
|
||||||
|
&& chmod u+x /usr/local/bin/install-*.sh \
|
||||||
|
&& chmod u+x /usr/local/bin/setup-image.sh \
|
||||||
|
&& install-mq-server-prereqs.sh \
|
||||||
|
&& setup-image.sh \
|
||||||
|
&& /opt/mqm/bin/security/amqpamcf \
|
||||||
|
&& chown -R 1001:root /opt/mqm/*
|
||||||
|
COPY --from=builder $GO_WORKDIR/runmqserver /usr/local/bin/
|
||||||
|
COPY --from=builder $GO_WORKDIR/chkmq* /usr/local/bin/
|
||||||
|
COPY NOTICES.txt /opt/mqm/licenses/notices-container.txt
|
||||||
|
COPY ha/native-ha.ini.tpl /etc/mqm/native-ha.ini.tpl
|
||||||
|
# Copy web XML files
|
||||||
|
COPY web /etc/mqm/web
|
||||||
|
COPY etc/mqm/*.tpl /etc/mqm/
|
||||||
|
RUN chmod ug+x /usr/local/bin/runmqserver \
|
||||||
|
&& chown 1001:root /usr/local/bin/*mq* \
|
||||||
|
&& chmod ug+x /usr/local/bin/chkmq* \
|
||||||
|
&& chown -R 1001:root /etc/mqm/* \
|
||||||
|
&& install --directory --mode 2775 --owner 1001 --group root /run/runmqserver \
|
||||||
|
&& touch /run/termination-log \
|
||||||
|
&& chown 1001:root /run/termination-log \
|
||||||
|
&& chmod 0660 /run/termination-log \
|
||||||
|
&& chmod -R g+w /etc/mqm/web
|
||||||
|
# Always use port 1414 for MQ & 9157 for the metrics
|
||||||
|
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
|
||||||
|
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
|
||||||
|
ENTRYPOINT ["runmqserver"]
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Build stage to build C code for custom authorization service (developer-only)
|
||||||
|
###############################################################################
|
||||||
|
# Use the Go toolset image, which already includes gcc and the MQ SDK
|
||||||
|
FROM builder as cbuilder
|
||||||
|
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
|
||||||
|
COPY authservice/ /opt/app-root/src/authservice/
|
||||||
|
WORKDIR /opt/app-root/src/authservice/mqhtpass
|
||||||
|
RUN make all
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Add default developer config
|
||||||
|
###############################################################################
|
||||||
|
FROM mq-server AS mq-dev-server
|
||||||
|
ARG BASE_IMAGE
|
||||||
|
ARG BASE_TAG
|
||||||
|
ARG GO_WORKDIR
|
||||||
|
LABEL summary="IBM MQ Advanced for Developers Server" \
|
||||||
|
description="Simplify, accelerate and facilitate the reliable exchange of data with a security-rich messaging solution — trusted by the world’s most successful enterprises" \
|
||||||
|
vendor="IBM" \
|
||||||
|
distribution-scope="private" \
|
||||||
|
authoritative-source-url="https://www.ibm.com/software/passportadvantage/" \
|
||||||
|
url="https://www.ibm.com/products/mq/advanced" \
|
||||||
|
io.openshift.tags="mq messaging" \
|
||||||
|
io.k8s.display-name="IBM MQ Advanced for Developers 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
|
||||||
|
USER 0
|
||||||
|
COPY --from=cbuilder /opt/app-root/src/authservice/mqhtpass/build/mqhtpass.so /opt/mqm/lib64/
|
||||||
|
COPY etc/mqm/*.ini /etc/mqm/
|
||||||
|
COPY etc/mqm/mq.htpasswd /etc/mqm/
|
||||||
|
COPY incubating/mqadvanced-server-dev/install-extra-packages.sh /usr/local/bin/
|
||||||
|
RUN chmod u+x /usr/local/bin/install-extra-packages.sh \
|
||||||
|
&& sleep 1 \
|
||||||
|
&& install-extra-packages.sh
|
||||||
|
COPY --from=builder $GO_WORKDIR/runmqdevserver /usr/local/bin/
|
||||||
|
# Copy template files
|
||||||
|
COPY incubating/mqadvanced-server-dev/*.tpl /etc/mqm/
|
||||||
|
# Copy web XML files for default developer configuration
|
||||||
|
COPY incubating/mqadvanced-server-dev/web /etc/mqm/web
|
||||||
|
RUN chown -R 1001:root /etc/mqm/* \
|
||||||
|
&& chmod -R g+w /etc/mqm/web \
|
||||||
|
&& chmod +x /usr/local/bin/runmq* \
|
||||||
|
&& chmod 0660 /etc/mqm/mq.htpasswd \
|
||||||
|
&& install --directory --mode 2775 --owner 1001 --group root /run/runmqdevserver
|
||||||
|
ENV MQ_DEV=true \
|
||||||
|
MQ_ENABLE_EMBEDDED_WEB_SERVER=1 \
|
||||||
|
MQ_GENERATE_CERTIFICATE_HOSTNAME=localhost \
|
||||||
|
LD_LIBRARY_PATH=/opt/mqm/lib64 \
|
||||||
|
MQ_CONNAUTH_USE_HTP=true \
|
||||||
|
MQS_PERMIT_UNKNOWN_ID=true
|
||||||
|
USER 1001
|
||||||
|
ENTRYPOINT ["runmqdevserver"]
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
ARG BASE_IMAGE
|
ARG BASE_IMAGE
|
||||||
|
|
||||||
# Build stage to build Go code
|
# Build stage to build Go code
|
||||||
FROM golang:1.10 as builder
|
FROM golang:1.22 as builder
|
||||||
WORKDIR /go/src/github.com/ibm-messaging/mq-container/
|
WORKDIR /go/src/github.com/ibm-messaging/mq-container/
|
||||||
COPY cmd/ ./cmd
|
COPY cmd/ ./cmd
|
||||||
COPY internal/ ./internal
|
COPY internal/ ./internal
|
||||||
|
|||||||
273
Makefile
273
Makefile
@@ -1,4 +1,4 @@
|
|||||||
# © Copyright IBM Corporation 2017, 2020
|
# © Copyright IBM Corporation 2017, 2023
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
@@ -18,7 +18,15 @@
|
|||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
include config.env
|
include config.env
|
||||||
|
include source-branch.env
|
||||||
|
|
||||||
|
# arch_uname is the platform architecture according to the uname program. Can be differ by OS, e.g. `arm64` on macOS, but `aarch64` on Linux.
|
||||||
|
arch_uname := $(shell uname -m)
|
||||||
|
# arch_go is the platform architecture in Go-style (e.g. amd64, ppc64le, s390x or arm64).
|
||||||
|
arch_go := $(if $(findstring x86_64,$(arch_uname)),amd64,$(if $(findstring aarch64,$(arch_uname)),arm64,$(arch_uname)))
|
||||||
|
# ARCH is the platform architecture in Go-style (e.g. amd64, ppc64le, s390x or arm64).
|
||||||
|
# Override this to build an image for a different architecture. Note that RUN instructions will not be able to succeed without the help of emulation provided by packages like qemu-user-static.
|
||||||
|
ARCH ?= $(arch_go)
|
||||||
# RELEASE shows what release of the container code has been built
|
# RELEASE shows what release of the container code has been built
|
||||||
RELEASE ?=
|
RELEASE ?=
|
||||||
# MQ_ARCHIVE_REPOSITORY is a remote repository from which to pull the MQ_ARCHIVE (if required)
|
# MQ_ARCHIVE_REPOSITORY is a remote repository from which to pull the MQ_ARCHIVE (if required)
|
||||||
@@ -37,10 +45,10 @@ MQ_ARCHIVE ?= IBM_MQ_$(MQ_VERSION_VRM)_$(MQ_ARCHIVE_TYPE)_$(MQ_ARCHIVE_ARCH)_NOI
|
|||||||
MQ_ARCHIVE_DEV ?= $(MQ_VERSION)-IBM-MQ-Advanced-for-Developers-Non-Install-$(MQ_ARCHIVE_DEV_TYPE)$(MQ_ARCHIVE_DEV_ARCH).tar.gz
|
MQ_ARCHIVE_DEV ?= $(MQ_VERSION)-IBM-MQ-Advanced-for-Developers-Non-Install-$(MQ_ARCHIVE_DEV_TYPE)$(MQ_ARCHIVE_DEV_ARCH).tar.gz
|
||||||
# MQ_SDK_ARCHIVE specifies the archive to use for building the golang programs. Defaults vary on developer or advanced.
|
# MQ_SDK_ARCHIVE specifies the archive to use for building the golang programs. Defaults vary on developer or advanced.
|
||||||
MQ_SDK_ARCHIVE ?= $(MQ_ARCHIVE_DEV_$(MQ_VERSION))
|
MQ_SDK_ARCHIVE ?= $(MQ_ARCHIVE_DEV_$(MQ_VERSION))
|
||||||
# Options to `go test` for the Docker tests
|
# Options to `go test` for the Container tests
|
||||||
TEST_OPTS_DOCKER ?=
|
TEST_OPTS_CONTAINER ?=
|
||||||
# Timeout for the Docker tests
|
# Timeout for the tests
|
||||||
TEST_TIMEOUT_DOCKER ?= 30m
|
TEST_TIMEOUT_CONTAINER ?= 45m
|
||||||
# MQ_IMAGE_ADVANCEDSERVER is the name of the built MQ Advanced image
|
# MQ_IMAGE_ADVANCEDSERVER is the name of the built MQ Advanced image
|
||||||
MQ_IMAGE_ADVANCEDSERVER ?=ibm-mqadvanced-server
|
MQ_IMAGE_ADVANCEDSERVER ?=ibm-mqadvanced-server
|
||||||
# MQ_IMAGE_DEVSERVER is the name of the built MQ Advanced for Developers image
|
# MQ_IMAGE_DEVSERVER is the name of the built MQ Advanced for Developers image
|
||||||
@@ -59,26 +67,26 @@ MQ_DELIVERY_REGISTRY_NAMESPACE ?=
|
|||||||
MQ_DELIVERY_REGISTRY_USER ?=
|
MQ_DELIVERY_REGISTRY_USER ?=
|
||||||
# MQ_DELIVERY_REGISTRY_CREDENTIAL is the password/API key for the remote registry (if required)
|
# MQ_DELIVERY_REGISTRY_CREDENTIAL is the password/API key for the remote registry (if required)
|
||||||
MQ_DELIVERY_REGISTRY_CREDENTIAL ?=
|
MQ_DELIVERY_REGISTRY_CREDENTIAL ?=
|
||||||
# REGISTRY_USER is the username used to login to the Red Hat registry
|
|
||||||
REGISTRY_USER ?=
|
|
||||||
# REGISTRY_PASS is the password used to login to the Red Hat registry
|
|
||||||
REGISTRY_PASS ?=
|
|
||||||
# ARCH is the platform architecture (e.g. amd64, ppc64le or s390x)
|
|
||||||
ARCH ?= $(if $(findstring x86_64,$(shell uname -m)),amd64,$(shell uname -m))
|
|
||||||
# LTS is a boolean value to enable/disable LTS container build
|
# LTS is a boolean value to enable/disable LTS container build
|
||||||
LTS ?= false
|
LTS ?= false
|
||||||
|
# VOLUME_MOUNT_OPTIONS is used when bind-mounting files from the "downloads" directory into the container. By default, SELinux labels are automatically re-written, but this doesn't work on some filesystems with extended attributes (xattrs). You can turn off the label re-writing by setting this variable to be blank.
|
||||||
|
VOLUME_MOUNT_OPTIONS ?= :Z
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# Other variables
|
# Other variables
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
# 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
|
GO_PKG_DIRS = ./cmd ./internal ./test
|
||||||
MQ_ARCHIVE_TYPE=LINUX
|
MQ_ARCHIVE_TYPE=LINUX
|
||||||
MQ_ARCHIVE_DEV_TYPE=Linux
|
MQ_ARCHIVE_DEV_TYPE=Linux
|
||||||
# BUILD_SERVER_CONTAINER is the name of the web server container used at build time
|
# BUILD_SERVER_CONTAINER is the name of the web server container used at build time
|
||||||
BUILD_SERVER_CONTAINER=build-server
|
BUILD_SERVER_CONTAINER=build-server
|
||||||
|
# BUILD_SERVER_NETWORK is the name of the network to use for the web server container used at build time
|
||||||
|
BUILD_SERVER_NETWORK=build
|
||||||
# NUM_CPU is the number of CPUs available to Docker. Used to control how many
|
# NUM_CPU is the number of CPUs available to Docker. Used to control how many
|
||||||
# test run in parallel
|
# test run in parallel
|
||||||
NUM_CPU ?= $(or $(shell docker info --format "{{ .NCPU }}"),2)
|
NUM_CPU ?= $(or $(shell $(COMMAND) info --format "{{ .NCPU }}"),2)
|
||||||
# BASE_IMAGE_TAG is a normalized version of BASE_IMAGE, suitable for use in a Docker tag
|
# BASE_IMAGE_TAG is a normalized version of BASE_IMAGE, suitable for use in a Docker tag
|
||||||
BASE_IMAGE_TAG=$(lastword $(subst /, ,$(subst :,-,$(BASE_IMAGE))))
|
BASE_IMAGE_TAG=$(lastword $(subst /, ,$(subst :,-,$(BASE_IMAGE))))
|
||||||
#BASE_IMAGE_TAG=$(subst /,-,$(subst :,-,$(BASE_IMAGE)))
|
#BASE_IMAGE_TAG=$(subst /,-,$(subst :,-,$(BASE_IMAGE)))
|
||||||
@@ -103,12 +111,22 @@ endif
|
|||||||
|
|
||||||
# Try to figure out which archive to use from the architecture
|
# Try to figure out which archive to use from the architecture
|
||||||
ifeq "$(ARCH)" "amd64"
|
ifeq "$(ARCH)" "amd64"
|
||||||
MQ_ARCHIVE_ARCH=X86-64
|
MQ_ARCHIVE_ARCH:=X86-64
|
||||||
MQ_ARCHIVE_DEV_ARCH=X64
|
MQ_ARCHIVE_DEV_ARCH:=X64
|
||||||
else ifeq "$(ARCH)" "ppc64le"
|
else ifeq "$(ARCH)" "ppc64le"
|
||||||
MQ_ARCHIVE_ARCH=PPC64LE
|
MQ_ARCHIVE_ARCH:=PPC64LE
|
||||||
|
MQ_ARCHIVE_DEV_ARCH:=PPC64LE
|
||||||
else ifeq "$(ARCH)" "s390x"
|
else ifeq "$(ARCH)" "s390x"
|
||||||
MQ_ARCHIVE_ARCH=S390X
|
MQ_ARCHIVE_ARCH:=S390X
|
||||||
|
MQ_ARCHIVE_DEV_ARCH:=S390X
|
||||||
|
else ifeq "$(ARCH)" "arm64"
|
||||||
|
MQ_ARCHIVE_ARCH:=ARM64
|
||||||
|
MQ_ARCHIVE_DEV_ARCH:=ARM64
|
||||||
|
endif
|
||||||
|
|
||||||
|
# If this is a fake master build, push images to alternative location (pipeline wont consider these images GA candidates)
|
||||||
|
ifeq ($(shell [ "$(TRAVIS)" = "true" ] && [ -n "$(MAIN_BRANCH)" ] && [ -n "$(SOURCE_BRANCH)" ] && [ "$(MAIN_BRANCH)" != "$(SOURCE_BRANCH)" ] && echo "true"), true)
|
||||||
|
MQ_DELIVERY_REGISTRY_NAMESPACE="master-fake"
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# LTS_TAG is the tag modifier for an LTS container build
|
# LTS_TAG is the tag modifier for an LTS container build
|
||||||
@@ -136,10 +154,16 @@ endif
|
|||||||
# image tagging
|
# image tagging
|
||||||
|
|
||||||
ifneq "$(RELEASE)" "$(EMPTY)"
|
ifneq "$(RELEASE)" "$(EMPTY)"
|
||||||
EXTRA_LABELS=--label release=$(RELEASE)
|
EXTRA_LABELS_RELEASE=--label "release=$(RELEASE)"
|
||||||
RELEASE_TAG="-$(RELEASE)"
|
RELEASE_TAG="-$(RELEASE)"
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifneq "$(MQ_ARCHIVE_LEVEL)" "$(EMPTY)"
|
||||||
|
EXTRA_LABELS_LEVEL=--label "mq-build=$(MQ_ARCHIVE_LEVEL)"
|
||||||
|
endif
|
||||||
|
|
||||||
|
EXTRA_LABELS=$(EXTRA_LABELS_RELEASE) $(EXTRA_LABELS_LEVEL)
|
||||||
|
|
||||||
ifeq "$(TIMESTAMPFLAT)" "$(EMPTY)"
|
ifeq "$(TIMESTAMPFLAT)" "$(EMPTY)"
|
||||||
TIMESTAMPFLAT=$(shell date "+%Y%m%d%H%M%S")
|
TIMESTAMPFLAT=$(shell date "+%Y%m%d%H%M%S")
|
||||||
endif
|
endif
|
||||||
@@ -152,6 +176,13 @@ ifeq ($(shell [ ! -z $(TRAVIS) ] && [ "$(TRAVIS_PULL_REQUEST)" = "false" ] && [
|
|||||||
MQ_MANIFEST_TAG_SUFFIX=.$(TIMESTAMPFLAT).$(GIT_COMMIT)
|
MQ_MANIFEST_TAG_SUFFIX=.$(TIMESTAMPFLAT).$(GIT_COMMIT)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
# Make sure we don't use VOLUME_MOUNT_OPTIONS for Podman on macOS
|
||||||
|
ifeq "$(COMMAND)" "podman"
|
||||||
|
ifeq "$(shell uname -s)" "Darwin"
|
||||||
|
VOLUME_MOUNT_OPTIONS:=
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
PATH_TO_MQ_TAG_CACHE=$(TRAVIS_BUILD_DIR)/.tagcache
|
PATH_TO_MQ_TAG_CACHE=$(TRAVIS_BUILD_DIR)/.tagcache
|
||||||
ifneq "$(TRAVIS)" "$(EMPTY)"
|
ifneq "$(TRAVIS)" "$(EMPTY)"
|
||||||
ifneq ("$(wildcard $(PATH_TO_MQ_TAG_CACHE))","")
|
ifneq ("$(wildcard $(PATH_TO_MQ_TAG_CACHE))","")
|
||||||
@@ -161,6 +192,7 @@ endif
|
|||||||
|
|
||||||
MQ_AMD64_TAG=$(MQ_MANIFEST_TAG)-amd64
|
MQ_AMD64_TAG=$(MQ_MANIFEST_TAG)-amd64
|
||||||
MQ_S390X_TAG?=$(MQ_MANIFEST_TAG)-s390x
|
MQ_S390X_TAG?=$(MQ_MANIFEST_TAG)-s390x
|
||||||
|
MQ_PPC64LE_TAG?=$(MQ_MANIFEST_TAG)-ppc64le
|
||||||
|
|
||||||
# end image tagging
|
# end image tagging
|
||||||
|
|
||||||
@@ -172,8 +204,10 @@ MQ_IMAGE_DEVSERVER_MANIFEST=$(MQ_IMAGE_DEVSERVER):$(MQ_MANIFEST_TAG)
|
|||||||
MQ_IMAGE_ADVANCEDSERVER_MANIFEST=$(MQ_IMAGE_ADVANCEDSERVER):$(MQ_MANIFEST_TAG)
|
MQ_IMAGE_ADVANCEDSERVER_MANIFEST=$(MQ_IMAGE_ADVANCEDSERVER):$(MQ_MANIFEST_TAG)
|
||||||
MQ_IMAGE_DEVSERVER_AMD64=$(MQ_DELIVERY_REGISTRY_FULL_PATH)/$(MQ_IMAGE_DEVSERVER):$(MQ_AMD64_TAG)
|
MQ_IMAGE_DEVSERVER_AMD64=$(MQ_DELIVERY_REGISTRY_FULL_PATH)/$(MQ_IMAGE_DEVSERVER):$(MQ_AMD64_TAG)
|
||||||
MQ_IMAGE_DEVSERVER_S390X=$(MQ_DELIVERY_REGISTRY_FULL_PATH)/$(MQ_IMAGE_DEVSERVER):$(MQ_S390X_TAG)
|
MQ_IMAGE_DEVSERVER_S390X=$(MQ_DELIVERY_REGISTRY_FULL_PATH)/$(MQ_IMAGE_DEVSERVER):$(MQ_S390X_TAG)
|
||||||
|
MQ_IMAGE_DEVSERVER_PPC64LE=$(MQ_DELIVERY_REGISTRY_FULL_PATH)/$(MQ_IMAGE_DEVSERVER):$(MQ_PPC64LE_TAG)
|
||||||
MQ_IMAGE_ADVANCEDSERVER_AMD64=$(MQ_DELIVERY_REGISTRY_FULL_PATH)/$(MQ_IMAGE_ADVANCEDSERVER):$(MQ_AMD64_TAG)
|
MQ_IMAGE_ADVANCEDSERVER_AMD64=$(MQ_DELIVERY_REGISTRY_FULL_PATH)/$(MQ_IMAGE_ADVANCEDSERVER):$(MQ_AMD64_TAG)
|
||||||
MQ_IMAGE_ADVANCEDSERVER_S390X=$(MQ_DELIVERY_REGISTRY_FULL_PATH)/$(MQ_IMAGE_ADVANCEDSERVER):$(MQ_S390X_TAG)
|
MQ_IMAGE_ADVANCEDSERVER_S390X=$(MQ_DELIVERY_REGISTRY_FULL_PATH)/$(MQ_IMAGE_ADVANCEDSERVER):$(MQ_S390X_TAG)
|
||||||
|
MQ_IMAGE_ADVANCEDSERVER_PPC64LE=$(MQ_DELIVERY_REGISTRY_FULL_PATH)/$(MQ_IMAGE_ADVANCEDSERVER):$(MQ_PPC64LE_TAG)
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# Build targets
|
# Build targets
|
||||||
@@ -203,16 +237,16 @@ downloads/$(MQ_ARCHIVE_DEV):
|
|||||||
mkdir -p downloads
|
mkdir -p downloads
|
||||||
ifneq "$(BUILD_RSYNC_SERVER)" "$(EMPTY)"
|
ifneq "$(BUILD_RSYNC_SERVER)" "$(EMPTY)"
|
||||||
# Use key which is not stored in the repository to fetch the files from the fileserver
|
# Use key which is not stored in the repository to fetch the files from the fileserver
|
||||||
curl -L $(BUILD_RSYNC_ENCRYPTED_KEY_URL) -o ./host.key.gpg
|
curl --fail --location $(BUILD_RSYNC_ENCRYPTED_KEY_URL) --output ./host.key.gpg
|
||||||
@echo $(BUILD_RSYNC_ENCRYPTION_PASSWORD)|gpg --passphrase-fd 0 ./host.key.gpg
|
@echo $(BUILD_RSYNC_ENCRYPTION_PASSWORD)|gpg --batch --passphrase-fd 0 ./host.key.gpg
|
||||||
chmod 600 ./host.key
|
chmod 600 ./host.key
|
||||||
rsync -rv -e "ssh -o BatchMode=yes -q -o StrictHostKeyChecking=no -i ./host.key" --include="*/" --include="*.tar.gz" --exclude="*" $(BUILD_RSYNC_USER)@$(BUILD_RSYNC_SERVER):"$(BUILD_RSYNC_PATH)" downloads/$(MQ_ARCHIVE_DEV)
|
rsync -rv -e "ssh -o BatchMode=yes -q -o StrictHostKeyChecking=no -i ./host.key" --include="*/" --include="*.tar.gz" --exclude="*" $(BUILD_RSYNC_USER)@$(BUILD_RSYNC_SERVER):"$(BUILD_RSYNC_PATH)" downloads/$(MQ_ARCHIVE_DEV)
|
||||||
-@rm host.key.gpg host.key
|
-@rm host.key.gpg host.key
|
||||||
else
|
else
|
||||||
ifneq "$(MQ_ARCHIVE_REPOSITORY_DEV)" "$(EMPTY)"
|
ifneq "$(MQ_ARCHIVE_REPOSITORY_DEV)" "$(EMPTY)"
|
||||||
curl -u $(MQ_ARCHIVE_REPOSITORY_USER):$(MQ_ARCHIVE_REPOSITORY_CREDENTIAL) -X GET "$(MQ_ARCHIVE_REPOSITORY_DEV)" -o downloads/$(MQ_ARCHIVE_DEV)
|
curl --fail --user $(MQ_ARCHIVE_REPOSITORY_USER):$(MQ_ARCHIVE_REPOSITORY_CREDENTIAL) --request GET "$(MQ_ARCHIVE_REPOSITORY_DEV)" --output downloads/$(MQ_ARCHIVE_DEV)
|
||||||
else
|
else
|
||||||
curl -L https://public.dhe.ibm.com/ibmdl/export/pub/software/websphere/messaging/mqadv/$(MQ_ARCHIVE_DEV) -o downloads/$(MQ_ARCHIVE_DEV)
|
curl --fail --location https://public.dhe.ibm.com/ibmdl/export/pub/software/websphere/messaging/mqadv/$(MQ_ARCHIVE_DEV) --output downloads/$(MQ_ARCHIVE_DEV)
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
@@ -222,14 +256,14 @@ downloads/$(MQ_ARCHIVE):
|
|||||||
ifneq "$(BUILD_RSYNC_SERVER)" "$(EMPTY)"
|
ifneq "$(BUILD_RSYNC_SERVER)" "$(EMPTY)"
|
||||||
# Use key which is not stored in the repository to fetch the files from the fileserver
|
# Use key which is not stored in the repository to fetch the files from the fileserver
|
||||||
-@rm host.key.gpg host.key
|
-@rm host.key.gpg host.key
|
||||||
curl -L $(BUILD_RSYNC_ENCRYPTED_KEY_URL) -o ./host.key.gpg
|
curl --fail --location $(BUILD_RSYNC_ENCRYPTED_KEY_URL) --output ./host.key.gpg
|
||||||
@echo $(BUILD_RSYNC_ENCRYPTION_PASSWORD)|gpg --passphrase-fd 0 ./host.key.gpg
|
@echo $(BUILD_RSYNC_ENCRYPTION_PASSWORD)|gpg --batch --passphrase-fd 0 ./host.key.gpg
|
||||||
chmod 600 ./host.key
|
chmod 600 ./host.key
|
||||||
rsync -rv -e "ssh -o BatchMode=yes -q -o StrictHostKeyChecking=no -i ./host.key" --include="*/" --include="*.tar.gz" --exclude="*" $(BUILD_RSYNC_USER)@$(BUILD_RSYNC_SERVER):"$(BUILD_RSYNC_PATH)" downloads/$(MQ_ARCHIVE)
|
rsync -rv -e "ssh -o BatchMode=yes -q -o StrictHostKeyChecking=no -i ./host.key" --include="*/" --include="*.tar.gz" --exclude="*" $(BUILD_RSYNC_USER)@$(BUILD_RSYNC_SERVER):"$(BUILD_RSYNC_PATH)" downloads/$(MQ_ARCHIVE)
|
||||||
-@rm host.key.gpg host.key
|
-@rm host.key.gpg host.key
|
||||||
else
|
else
|
||||||
ifneq "$(MQ_ARCHIVE_REPOSITORY)" "$(EMPTY)"
|
ifneq "$(MQ_ARCHIVE_REPOSITORY)" "$(EMPTY)"
|
||||||
curl -u $(MQ_ARCHIVE_REPOSITORY_USER):$(MQ_ARCHIVE_REPOSITORY_CREDENTIAL) -X GET "$(MQ_ARCHIVE_REPOSITORY)" -o downloads/$(MQ_ARCHIVE)
|
curl --fail --user $(MQ_ARCHIVE_REPOSITORY_USER):$(MQ_ARCHIVE_REPOSITORY_CREDENTIAL) --request GET "$(MQ_ARCHIVE_REPOSITORY)" --output downloads/$(MQ_ARCHIVE)
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
@@ -240,39 +274,43 @@ downloads: downloads/$(MQ_ARCHIVE_DEV) downloads/$(MQ_SDK_ARCHIVE)
|
|||||||
cache-mq-tag:
|
cache-mq-tag:
|
||||||
@printf "MQ_MANIFEST_TAG=$(MQ_MANIFEST_TAG)\n" | tee $(PATH_TO_MQ_TAG_CACHE)
|
@printf "MQ_MANIFEST_TAG=$(MQ_MANIFEST_TAG)\n" | tee $(PATH_TO_MQ_TAG_CACHE)
|
||||||
|
|
||||||
# Vendor Go dependencies for the Docker tests
|
###############################################################################
|
||||||
test/docker/vendor:
|
# Test targets
|
||||||
cd test/docker && go mod vendor
|
###############################################################################
|
||||||
|
|
||||||
|
# Vendor Go dependencies for the Container tests
|
||||||
|
test/container/vendor:
|
||||||
|
cd test/container && go mod vendor
|
||||||
|
|
||||||
# Shortcut to just run the unit tests
|
# Shortcut to just run the unit tests
|
||||||
.PHONY: test-unit
|
.PHONY: test-unit
|
||||||
test-unit:
|
test-unit:
|
||||||
docker build --target builder --file Dockerfile-server .
|
$(COMMAND) build --target builder --file Dockerfile-server .
|
||||||
|
|
||||||
.PHONY: test-advancedserver
|
.PHONY: test-advancedserver
|
||||||
test-advancedserver: test/docker/vendor
|
test-advancedserver: test/container/vendor
|
||||||
$(info $(SPACER)$(shell printf $(TITLE)"Test $(MQ_IMAGE_ADVANCEDSERVER):$(MQ_TAG) on $(shell docker --version)"$(END)))
|
$(info $(SPACER)$(shell printf $(TITLE)"Test $(MQ_IMAGE_ADVANCEDSERVER):$(MQ_TAG) on $(shell $(COMMAND) --version)"$(END)))
|
||||||
docker inspect $(MQ_IMAGE_ADVANCEDSERVER):$(MQ_TAG)
|
$(COMMAND) inspect $(MQ_IMAGE_ADVANCEDSERVER):$(MQ_TAG)
|
||||||
cd test/docker && TEST_IMAGE=$(MQ_IMAGE_ADVANCEDSERVER):$(MQ_TAG) EXPECTED_LICENSE=Production go test -parallel $(NUM_CPU) -timeout $(TEST_TIMEOUT_DOCKER) $(TEST_OPTS_DOCKER)
|
cd test/container && TEST_IMAGE=$(MQ_IMAGE_ADVANCEDSERVER):$(MQ_TAG) EXPECTED_LICENSE=Production DOCKER_API_VERSION=$(DOCKER_API_VERSION) COMMAND=$(COMMAND) go test -parallel $(NUM_CPU) -timeout $(TEST_TIMEOUT_CONTAINER) $(TEST_OPTS_CONTAINER)
|
||||||
|
|
||||||
.PHONY: build-devjmstest
|
.PHONY: build-devjmstest
|
||||||
build-devjmstest:
|
build-devjmstest:
|
||||||
$(info $(SPACER)$(shell printf $(TITLE)"Build JMS tests for developer config"$(END)))
|
$(info $(SPACER)$(shell printf $(TITLE)"Build JMS tests for developer config"$(END)))
|
||||||
cd test/messaging && docker build --tag $(DEV_JMS_IMAGE) .
|
cd test/messaging && $(COMMAND) build --tag $(DEV_JMS_IMAGE) .
|
||||||
|
|
||||||
.PHONY: test-devserver
|
.PHONY: test-devserver
|
||||||
test-devserver: test/docker/vendor
|
test-devserver: test/container/vendor
|
||||||
$(info $(SPACER)$(shell printf $(TITLE)"Test $(MQ_IMAGE_DEVSERVER):$(MQ_TAG) on $(shell docker --version)"$(END)))
|
$(info $(SPACER)$(shell printf $(TITLE)"Test $(MQ_IMAGE_DEVSERVER):$(MQ_TAG) on $(shell $(COMMAND) --version)"$(END)))
|
||||||
docker inspect $(MQ_IMAGE_DEVSERVER):$(MQ_TAG)
|
$(COMMAND) inspect $(MQ_IMAGE_DEVSERVER):$(MQ_TAG)
|
||||||
cd test/docker && TEST_IMAGE=$(MQ_IMAGE_DEVSERVER):$(MQ_TAG) EXPECTED_LICENSE=Developer DEV_JMS_IMAGE=$(DEV_JMS_IMAGE) IBMJRE=true go test -parallel $(NUM_CPU) -timeout $(TEST_TIMEOUT_DOCKER) -tags mqdev $(TEST_OPTS_DOCKER)
|
cd test/container && TEST_IMAGE=$(MQ_IMAGE_DEVSERVER):$(MQ_TAG) EXPECTED_LICENSE=Developer DEV_JMS_IMAGE=$(DEV_JMS_IMAGE) IBMJRE=false DOCKER_API_VERSION=$(DOCKER_API_VERSION) COMMAND=$(COMMAND) go test -parallel $(NUM_CPU) -timeout $(TEST_TIMEOUT_CONTAINER) -tags mqdev $(TEST_OPTS_CONTAINER)
|
||||||
|
|
||||||
.PHONY: coverage
|
.PHONY: coverage
|
||||||
coverage:
|
coverage:
|
||||||
mkdir coverage
|
mkdir coverage
|
||||||
|
|
||||||
.PHONY: test-advancedserver-cover
|
.PHONY: test-advancedserver-cover
|
||||||
test-advancedserver-cover: test/docker/vendor coverage
|
test-advancedserver-cover: test/container/vendor coverage
|
||||||
$(info $(SPACER)$(shell printf $(TITLE)"Test $(MQ_IMAGE_ADVANCEDSERVER):$(MQ_TAG) with code coverage on $(shell docker --version)"$(END)))
|
$(info $(SPACER)$(shell printf $(TITLE)"Test $(MQ_IMAGE_ADVANCEDSERVER):$(MQ_TAG) with code coverage on $(shell $(COMMAND) --version)"$(END)))
|
||||||
rm -f ./coverage/unit*.cov
|
rm -f ./coverage/unit*.cov
|
||||||
# Run unit tests with coverage, for each package under 'internal'
|
# Run unit tests with coverage, for each package under 'internal'
|
||||||
go list -f '{{.Name}}' ./internal/... | xargs -I {} go test -cover -covermode count -coverprofile ./coverage/unit-{}.cov ./internal/{}
|
go list -f '{{.Name}}' ./internal/... | xargs -I {} go test -cover -covermode count -coverprofile ./coverage/unit-{}.cov ./internal/{}
|
||||||
@@ -281,65 +319,57 @@ test-advancedserver-cover: test/docker/vendor coverage
|
|||||||
tail -q -n +2 ./coverage/unit-*.cov >> ./coverage/unit.cov
|
tail -q -n +2 ./coverage/unit-*.cov >> ./coverage/unit.cov
|
||||||
go tool cover -html=./coverage/unit.cov -o ./coverage/unit.html
|
go tool cover -html=./coverage/unit.cov -o ./coverage/unit.html
|
||||||
|
|
||||||
rm -f ./test/docker/coverage/*.cov
|
rm -f ./test/container/coverage/*.cov
|
||||||
rm -f ./coverage/docker.*
|
rm -f ./coverage/container.*
|
||||||
mkdir -p ./test/docker/coverage/
|
mkdir -p ./test/container/coverage/
|
||||||
cd test/docker && TEST_IMAGE=$(MQ_IMAGE_ADVANCEDSERVER):$(MQ_TAG)-cover TEST_COVER=true go test $(TEST_OPTS_DOCKER)
|
cd test/container && TEST_IMAGE=$(MQ_IMAGE_ADVANCEDSERVER):$(MQ_TAG)-cover TEST_COVER=true DOCKER_API_VERSION=$(DOCKER_API_VERSION) go test $(TEST_OPTS_CONTAINER)
|
||||||
echo 'mode: count' > ./coverage/docker.cov
|
echo 'mode: count' > ./coverage/container.cov
|
||||||
tail -q -n +2 ./test/docker/coverage/*.cov >> ./coverage/docker.cov
|
tail -q -n +2 ./test/container/coverage/*.cov >> ./coverage/container.cov
|
||||||
go tool cover -html=./coverage/docker.cov -o ./coverage/docker.html
|
go tool cover -html=./coverage/container.cov -o ./coverage/container.html
|
||||||
|
|
||||||
echo 'mode: count' > ./coverage/combined.cov
|
echo 'mode: count' > ./coverage/combined.cov
|
||||||
tail -q -n +2 ./coverage/unit.cov ./coverage/docker.cov >> ./coverage/combined.cov
|
tail -q -n +2 ./coverage/unit.cov ./coverage/container.cov >> ./coverage/combined.cov
|
||||||
go tool cover -html=./coverage/combined.cov -o ./coverage/combined.html
|
go tool cover -html=./coverage/combined.cov -o ./coverage/combined.html
|
||||||
|
|
||||||
# Build an MQ image. The commands used are slightly different between Docker and Podman
|
###############################################################################
|
||||||
|
# Build functions
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
# 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
|
||||||
|
# Args: imageName, imageTag, dockerfile, mqArchive, dockerfileTarget
|
||||||
define build-mq
|
define build-mq
|
||||||
$(if $(findstring docker,$(COMMAND)), @docker network create build,)
|
rm -f .dockerignore && echo ".git\ndownloads\n!downloads/$4" > .dockerignore
|
||||||
$(if $(findstring docker,$(COMMAND)), @docker run --rm --name $(BUILD_SERVER_CONTAINER) --network build --network-alias build --volume $(DOWNLOADS_DIR):/opt/app-root/src:ro --detach registry.redhat.io/ubi8/nginx-118 nginx -g "daemon off;",)
|
|
||||||
$(eval EXTRA_ARGS=$(if $(findstring docker,$(COMMAND)), --network build --build-arg MQ_URL=http://build:8080/$4, --volume $(DOWNLOADS_DIR):/var/downloads --build-arg MQ_URL=file:///var/downloads/$4))
|
|
||||||
# Build the new image
|
|
||||||
$(COMMAND) build \
|
$(COMMAND) build \
|
||||||
--tag $1:$2 \
|
--tag $1:$2 \
|
||||||
--file $3 \
|
--file $3 \
|
||||||
$(EXTRA_ARGS) \
|
|
||||||
--build-arg IMAGE_REVISION="$(IMAGE_REVISION)" \
|
--build-arg IMAGE_REVISION="$(IMAGE_REVISION)" \
|
||||||
--build-arg IMAGE_SOURCE="$(IMAGE_SOURCE)" \
|
--build-arg IMAGE_SOURCE="$(IMAGE_SOURCE)" \
|
||||||
--build-arg IMAGE_TAG="$1:$2" \
|
--build-arg IMAGE_TAG="$1:$2" \
|
||||||
|
--build-arg MQ_ARCHIVE="downloads/$4" \
|
||||||
--label version=$(MQ_VERSION) \
|
--label version=$(MQ_VERSION) \
|
||||||
--label name=$1 \
|
--label name=$1 \
|
||||||
--label build-date=$(shell date +%Y-%m-%dT%H:%M:%S%z) \
|
--label build-date=$(shell date +%Y-%m-%dT%H:%M:%S%z) \
|
||||||
--label architecture="$(ARCH)" \
|
--label architecture="$(ARCH)" \
|
||||||
--label run="docker run -d -e LICENSE=accept $1:$2" \
|
--label run="podman run -d -e LICENSE=accept $1:$2" \
|
||||||
--label vcs-ref=$(IMAGE_REVISION) \
|
--label vcs-ref=$(IMAGE_REVISION) \
|
||||||
--label vcs-type=git \
|
--label vcs-type=git \
|
||||||
--label vcs-url=$(IMAGE_SOURCE) \
|
--label vcs-url=$(IMAGE_SOURCE) \
|
||||||
|
$(if $(findstring $(arch_go),$(ARCH)),,--platform=linux/$(ARCH)) \
|
||||||
$(EXTRA_LABELS) \
|
$(EXTRA_LABELS) \
|
||||||
--target $5 \
|
--target $5 \
|
||||||
.
|
.
|
||||||
$(if $(findstring docker,$(COMMAND)), @docker kill $(BUILD_SERVER_CONTAINER))
|
|
||||||
$(if $(findstring docker,$(COMMAND)), @docker network rm build)
|
|
||||||
endef
|
endef
|
||||||
|
|
||||||
DOCKER_SERVER_VERSION=$(shell docker version --format "{{ .Server.Version }}")
|
###############################################################################
|
||||||
DOCKER_CLIENT_VERSION=$(shell docker version --format "{{ .Client.Version }}")
|
# Build targets
|
||||||
PODMAN_VERSION=$(shell podman version --format "{{ .Version }}")
|
###############################################################################
|
||||||
.PHONY: command-version
|
|
||||||
command-version:
|
|
||||||
# If we're using Docker, then check it's recent enough to support multi-stage builds
|
|
||||||
ifneq (,$(findstring docker,$(COMMAND)))
|
|
||||||
@test "$(word 1,$(subst ., ,$(DOCKER_CLIENT_VERSION)))" -ge "17" || ("$(word 1,$(subst ., ,$(DOCKER_CLIENT_VERSION)))" -eq "17" && "$(word 2,$(subst ., ,$(DOCKER_CLIENT_VERSION)))" -ge "05") || (echo "Error: Docker client 17.05 or greater is required" && exit 1)
|
|
||||||
@test "$(word 1,$(subst ., ,$(DOCKER_SERVER_VERSION)))" -ge "17" || ("$(word 1,$(subst ., ,$(DOCKER_SERVER_VERSION)))" -eq "17" && "$(word 2,$(subst ., ,$(DOCKER_CLIENT_VERSION)))" -ge "05") || (echo "Error: Docker server 17.05 or greater is required" && exit 1)
|
|
||||||
endif
|
|
||||||
ifneq (,$(findstring podman,$(COMMAND)))
|
|
||||||
@test "$(word 1,$(subst ., ,$(PODMAN_VERSION)))" -ge "1" || (echo "Error: Podman version 1.0 or greater is required" && exit 1)
|
|
||||||
endif
|
|
||||||
|
|
||||||
.PHONY: build-advancedserver-host
|
.PHONY: build-advancedserver-host
|
||||||
build-advancedserver-host: build-advancedserver
|
build-advancedserver-host: build-advancedserver
|
||||||
|
|
||||||
.PHONY: build-advancedserver
|
.PHONY: build-advancedserver
|
||||||
build-advancedserver: registry-login log-build-env downloads/$(MQ_ARCHIVE) command-version
|
build-advancedserver: log-build-env downloads/$(MQ_ARCHIVE) command-version
|
||||||
$(info $(SPACER)$(shell printf $(TITLE)"Build $(MQ_IMAGE_ADVANCEDSERVER):$(MQ_TAG)"$(END)))
|
$(info $(SPACER)$(shell printf $(TITLE)"Build $(MQ_IMAGE_ADVANCEDSERVER):$(MQ_TAG)"$(END)))
|
||||||
$(call build-mq,$(MQ_IMAGE_ADVANCEDSERVER),$(MQ_TAG),Dockerfile-server,$(MQ_ARCHIVE),mq-server)
|
$(call build-mq,$(MQ_IMAGE_ADVANCEDSERVER),$(MQ_TAG),Dockerfile-server,$(MQ_ARCHIVE),mq-server)
|
||||||
|
|
||||||
@@ -347,40 +377,39 @@ build-advancedserver: registry-login log-build-env downloads/$(MQ_ARCHIVE) comma
|
|||||||
build-devserver-host: build-devserver
|
build-devserver-host: build-devserver
|
||||||
|
|
||||||
.PHONY: build-devserver
|
.PHONY: build-devserver
|
||||||
build-devserver: registry-login log-build-env downloads/$(MQ_ARCHIVE_DEV) command-version
|
build-devserver: log-build-env downloads/$(MQ_ARCHIVE_DEV) command-version
|
||||||
$(info $(shell printf $(TITLE)"Build $(MQ_IMAGE_DEVSERVER):$(MQ_TAG)"$(END)))
|
$(info $(shell printf $(TITLE)"Build $(MQ_IMAGE_DEVSERVER):$(MQ_TAG)"$(END)))
|
||||||
$(call build-mq,$(MQ_IMAGE_DEVSERVER),$(MQ_TAG),Dockerfile-server,$(MQ_ARCHIVE_DEV),mq-dev-server)
|
$(call build-mq,$(MQ_IMAGE_DEVSERVER),$(MQ_TAG),Dockerfile-server,$(MQ_ARCHIVE_DEV),mq-dev-server)
|
||||||
|
|
||||||
.PHONY: build-advancedserver-cover
|
.PHONY: build-advancedserver-cover
|
||||||
build-advancedserver-cover: registry-login command-version
|
build-advancedserver-cover: command-version
|
||||||
$(COMMAND) build --build-arg BASE_IMAGE=$(MQ_IMAGE_ADVANCEDSERVER):$(MQ_TAG) -t $(MQ_IMAGE_ADVANCEDSERVER):$(MQ_TAG)-cover -f Dockerfile-server.cover .
|
$(COMMAND) build --build-arg BASE_IMAGE=$(MQ_IMAGE_ADVANCEDSERVER):$(MQ_TAG) -t $(MQ_IMAGE_ADVANCEDSERVER):$(MQ_TAG)-cover -f Dockerfile-server.cover .
|
||||||
|
|
||||||
.PHONY: build-explorer
|
.PHONY: build-explorer
|
||||||
build-explorer: registry-login downloads/$(MQ_ARCHIVE_DEV)
|
build-explorer: downloads/$(MQ_ARCHIVE_DEV)
|
||||||
$(call build-mq,mq-explorer,latest-$(ARCH),incubating/mq-explorer/Dockerfile,$(MQ_ARCHIVE_DEV),mq-explorer)
|
$(call build-mq,mq-explorer,latest-$(ARCH),incubating/mq-explorer/Dockerfile,$(MQ_ARCHIVE_DEV),mq-explorer)
|
||||||
|
|
||||||
.PHONY: build-sdk
|
.PHONY: build-sdk
|
||||||
build-sdk: registry-login downloads/$(MQ_ARCHIVE_DEV)
|
build-sdk: downloads/$(MQ_ARCHIVE_DEV)
|
||||||
$(info $(shell printf $(TITLE)"Build $(MQ_IMAGE_SDK)"$(END)))
|
$(info $(shell printf $(TITLE)"Build $(MQ_IMAGE_SDK)"$(END)))
|
||||||
$(call build-mq,mq-sdk,$(MQ_TAG),incubating/mq-sdk/Dockerfile,$(MQ_SDK_ARCHIVE),mq-sdk)
|
$(call build-mq,mq-sdk,$(MQ_TAG),incubating/mq-sdk/Dockerfile,$(MQ_SDK_ARCHIVE),mq-sdk)
|
||||||
|
|
||||||
.PHONY: registry-login
|
###############################################################################
|
||||||
registry-login:
|
# Logging targets
|
||||||
ifneq ($(REGISTRY_USER),)
|
###############################################################################
|
||||||
$(COMMAND) login -u $(REGISTRY_USER) -p $(REGISTRY_PASS) registry.redhat.io
|
|
||||||
endif
|
|
||||||
|
|
||||||
.PHONY: log-build-env
|
.PHONY: log-build-env
|
||||||
log-build-vars:
|
log-build-vars:
|
||||||
$(info $(SPACER)$(shell printf $(TITLE)"Build environment"$(END)))
|
$(info $(SPACER)$(shell printf $(TITLE)"Build environment"$(END)))
|
||||||
@echo ARCH=$(ARCH)
|
@echo arch_uname=$(arch_uname)
|
||||||
@echo MQ_VERSION=$(MQ_VERSION)
|
@echo arch_go=$(arch_go)
|
||||||
@echo MQ_ARCHIVE=$(MQ_ARCHIVE)
|
@echo "ARCH=$(ARCH) (origin:$(origin ARCH))"
|
||||||
|
@echo MQ_VERSION="$(MQ_VERSION) (origin:$(origin MQ_VERSION))"
|
||||||
|
@echo MQ_ARCHIVE="$(MQ_ARCHIVE) (origin:$(origin MQ_ARCHIVE))"
|
||||||
|
@echo MQ_ARCHIVE_DEV_ARCH=$(MQ_ARCHIVE_DEV_ARCH)
|
||||||
@echo MQ_ARCHIVE_DEV=$(MQ_ARCHIVE_DEV)
|
@echo MQ_ARCHIVE_DEV=$(MQ_ARCHIVE_DEV)
|
||||||
@echo MQ_IMAGE_DEVSERVER=$(MQ_IMAGE_DEVSERVER)
|
@echo MQ_IMAGE_DEVSERVER=$(MQ_IMAGE_DEVSERVER)
|
||||||
@echo MQ_IMAGE_ADVANCEDSERVER=$(MQ_IMAGE_ADVANCEDSERVER)
|
@echo MQ_IMAGE_ADVANCEDSERVER=$(MQ_IMAGE_ADVANCEDSERVER)
|
||||||
@echo COMMAND=$(COMMAND)
|
@echo COMMAND=$(COMMAND)
|
||||||
@echo REGISTRY_USER=$(REGISTRY_USER)
|
|
||||||
|
|
||||||
.PHONY: log-build-env
|
.PHONY: log-build-env
|
||||||
log-build-env: log-build-vars
|
log-build-env: log-build-vars
|
||||||
@@ -390,16 +419,22 @@ log-build-env: log-build-vars
|
|||||||
|
|
||||||
include formatting.mk
|
include formatting.mk
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Push/pull targets
|
||||||
|
###############################################################################
|
||||||
.PHONY: pull-mq-archive
|
.PHONY: pull-mq-archive
|
||||||
pull-mq-archive:
|
pull-mq-archive:
|
||||||
curl -u $(MQ_ARCHIVE_REPOSITORY_USER):$(MQ_ARCHIVE_REPOSITORY_CREDENTIAL) -X GET "$(MQ_ARCHIVE_REPOSITORY)" -o downloads/$(MQ_ARCHIVE)
|
curl --fail --user $(MQ_ARCHIVE_REPOSITORY_USER):$(MQ_ARCHIVE_REPOSITORY_CREDENTIAL) --request GET "$(MQ_ARCHIVE_REPOSITORY)" --output downloads/$(MQ_ARCHIVE)
|
||||||
|
|
||||||
.PHONY: pull-mq-archive-dev
|
.PHONY: pull-mq-archive-dev
|
||||||
pull-mq-archive-dev:
|
pull-mq-archive-dev:
|
||||||
curl -u $(MQ_ARCHIVE_REPOSITORY_USER):$(MQ_ARCHIVE_REPOSITORY_CREDENTIAL) -X GET "$(MQ_ARCHIVE_REPOSITORY_DEV)" -o downloads/$(MQ_ARCHIVE_DEV)
|
curl --fail --user $(MQ_ARCHIVE_REPOSITORY_USER):$(MQ_ARCHIVE_REPOSITORY_CREDENTIAL) --request GET "$(MQ_ARCHIVE_REPOSITORY_DEV)" --output downloads/$(MQ_ARCHIVE_DEV)
|
||||||
|
|
||||||
.PHONY: push-advancedserver
|
.PHONY: push-advancedserver
|
||||||
push-advancedserver:
|
push-advancedserver:
|
||||||
|
@if [ $(MQ_DELIVERY_REGISTRY_NAMESPACE) = "master-fake" ]; then\
|
||||||
|
echo "Detected fake master build. Note that the push destination is set to the fake master namespace: $(MQ_DELIVERY_REGISTRY_FULL_PATH)";\
|
||||||
|
fi
|
||||||
$(info $(SPACER)$(shell printf $(TITLE)"Push production image to $(MQ_DELIVERY_REGISTRY_FULL_PATH)"$(END)))
|
$(info $(SPACER)$(shell printf $(TITLE)"Push production image to $(MQ_DELIVERY_REGISTRY_FULL_PATH)"$(END)))
|
||||||
$(COMMAND) login $(MQ_DELIVERY_REGISTRY_HOSTNAME) -u $(MQ_DELIVERY_REGISTRY_USER) -p $(MQ_DELIVERY_REGISTRY_CREDENTIAL)
|
$(COMMAND) login $(MQ_DELIVERY_REGISTRY_HOSTNAME) -u $(MQ_DELIVERY_REGISTRY_USER) -p $(MQ_DELIVERY_REGISTRY_CREDENTIAL)
|
||||||
$(COMMAND) tag $(MQ_IMAGE_ADVANCEDSERVER)\:$(MQ_TAG) $(MQ_DELIVERY_REGISTRY_FULL_PATH)/$(MQ_IMAGE_FULL_RELEASE_NAME)
|
$(COMMAND) tag $(MQ_IMAGE_ADVANCEDSERVER)\:$(MQ_TAG) $(MQ_DELIVERY_REGISTRY_FULL_PATH)/$(MQ_IMAGE_FULL_RELEASE_NAME)
|
||||||
@@ -407,6 +442,9 @@ push-advancedserver:
|
|||||||
|
|
||||||
.PHONY: push-devserver
|
.PHONY: push-devserver
|
||||||
push-devserver:
|
push-devserver:
|
||||||
|
@if [ $(MQ_DELIVERY_REGISTRY_NAMESPACE) = "master-fake" ]; then\
|
||||||
|
echo "Detected fake master build. Note that the push destination is set to the fake master namespace: $(MQ_DELIVERY_REGISTRY_FULL_PATH)";\
|
||||||
|
fi
|
||||||
$(info $(SPACER)$(shell printf $(TITLE)"Push developer image to $(MQ_DELIVERY_REGISTRY_FULL_PATH)"$(END)))
|
$(info $(SPACER)$(shell printf $(TITLE)"Push developer image to $(MQ_DELIVERY_REGISTRY_FULL_PATH)"$(END)))
|
||||||
$(COMMAND) login $(MQ_DELIVERY_REGISTRY_HOSTNAME) -u $(MQ_DELIVERY_REGISTRY_USER) -p $(MQ_DELIVERY_REGISTRY_CREDENTIAL)
|
$(COMMAND) login $(MQ_DELIVERY_REGISTRY_HOSTNAME) -u $(MQ_DELIVERY_REGISTRY_USER) -p $(MQ_DELIVERY_REGISTRY_CREDENTIAL)
|
||||||
$(COMMAND) tag $(MQ_IMAGE_DEVSERVER)\:$(MQ_TAG) $(MQ_DELIVERY_REGISTRY_FULL_PATH)/$(MQ_IMAGE_DEV_FULL_RELEASE_NAME)
|
$(COMMAND) tag $(MQ_IMAGE_DEVSERVER)\:$(MQ_TAG) $(MQ_DELIVERY_REGISTRY_FULL_PATH)/$(MQ_IMAGE_DEV_FULL_RELEASE_NAME)
|
||||||
@@ -430,25 +468,33 @@ pull-devserver:
|
|||||||
push-manifest: build-skopeo-container
|
push-manifest: build-skopeo-container
|
||||||
$(info $(SPACER)$(shell printf $(TITLE)"** Determining the image digests **"$(END)))
|
$(info $(SPACER)$(shell printf $(TITLE)"** Determining the image digests **"$(END)))
|
||||||
ifneq "$(LTS)" "true"
|
ifneq "$(LTS)" "true"
|
||||||
$(eval MQ_IMAGE_DEVSERVER_AMD64_DIGEST=$(shell $(COMMAND) run skopeo:latest --override-os linux --override-arch s390x inspect --creds $(MQ_ARCHIVE_REPOSITORY_USER):$(MQ_ARCHIVE_REPOSITORY_CREDENTIAL) docker://$(MQ_IMAGE_DEVSERVER_AMD64) | jq -r .Digest))
|
$(eval MQ_IMAGE_DEVSERVER_AMD64_DIGEST=$(shell $(COMMAND) run skopeo:latest --override-os linux inspect --creds $(MQ_ARCHIVE_REPOSITORY_USER):$(MQ_ARCHIVE_REPOSITORY_CREDENTIAL) docker://$(MQ_IMAGE_DEVSERVER_AMD64) | jq -r .Digest))
|
||||||
$(eval MQ_IMAGE_DEVSERVER_S390X_DIGEST=$(shell $(COMMAND) run skopeo:latest --override-os linux inspect --creds $(MQ_ARCHIVE_REPOSITORY_USER):$(MQ_ARCHIVE_REPOSITORY_CREDENTIAL) docker://$(MQ_IMAGE_DEVSERVER_S390X) | jq -r .Digest))
|
$(eval MQ_IMAGE_DEVSERVER_S390X_DIGEST=$(shell $(COMMAND) run skopeo:latest --override-os linux inspect --creds $(MQ_ARCHIVE_REPOSITORY_USER):$(MQ_ARCHIVE_REPOSITORY_CREDENTIAL) docker://$(MQ_IMAGE_DEVSERVER_S390X) | jq -r .Digest))
|
||||||
|
$(eval MQ_IMAGE_DEVSERVER_PPC64LE_DIGEST=$(shell $(COMMAND) run skopeo:latest --override-os linux inspect --creds $(MQ_ARCHIVE_REPOSITORY_USER):$(MQ_ARCHIVE_REPOSITORY_CREDENTIAL) docker://$(MQ_IMAGE_DEVSERVER_PPC64LE) | jq -r .Digest))
|
||||||
$(info $(shell printf "** Determined the built $(MQ_IMAGE_DEVSERVER_AMD64) has a digest of $(MQ_IMAGE_DEVSERVER_AMD64_DIGEST)**"$(END)))
|
$(info $(shell printf "** Determined the built $(MQ_IMAGE_DEVSERVER_AMD64) has a digest of $(MQ_IMAGE_DEVSERVER_AMD64_DIGEST)**"$(END)))
|
||||||
$(info $(shell printf "** Determined the built $(MQ_IMAGE_DEVSERVER_S390X) has a digest of $(MQ_IMAGE_DEVSERVER_S390X_DIGEST)**"$(END)))
|
$(info $(shell printf "** Determined the built $(MQ_IMAGE_DEVSERVER_S390X) has a digest of $(MQ_IMAGE_DEVSERVER_S390X_DIGEST)**"$(END)))
|
||||||
|
$(info $(shell printf "** Determined the built $(MQ_IMAGE_DEVSERVER_PPC64LE) has a digest of $(MQ_IMAGE_DEVSERVER_PPC64LE_DIGEST)**"$(END)))
|
||||||
endif
|
endif
|
||||||
$(eval MQ_IMAGE_ADVANCEDSERVER_AMD64_DIGEST=$(shell $(COMMAND) run skopeo:latest --override-os linux inspect --creds $(MQ_ARCHIVE_REPOSITORY_USER):$(MQ_ARCHIVE_REPOSITORY_CREDENTIAL) docker://$(MQ_IMAGE_ADVANCEDSERVER_AMD64) | jq -r .Digest))
|
$(eval MQ_IMAGE_ADVANCEDSERVER_AMD64_DIGEST=$(shell $(COMMAND) run skopeo:latest --override-os linux inspect --creds $(MQ_ARCHIVE_REPOSITORY_USER):$(MQ_ARCHIVE_REPOSITORY_CREDENTIAL) docker://$(MQ_IMAGE_ADVANCEDSERVER_AMD64) | jq -r .Digest))
|
||||||
$(eval MQ_IMAGE_ADVANCEDSERVER_S390X_DIGEST=$(shell $(COMMAND) run skopeo:latest --override-os linux inspect --creds $(MQ_ARCHIVE_REPOSITORY_USER):$(MQ_ARCHIVE_REPOSITORY_CREDENTIAL) docker://$(MQ_IMAGE_ADVANCEDSERVER_S390X) | jq -r .Digest))
|
$(eval MQ_IMAGE_ADVANCEDSERVER_S390X_DIGEST=$(shell $(COMMAND) run skopeo:latest --override-os linux inspect --creds $(MQ_ARCHIVE_REPOSITORY_USER):$(MQ_ARCHIVE_REPOSITORY_CREDENTIAL) docker://$(MQ_IMAGE_ADVANCEDSERVER_S390X) | jq -r .Digest))
|
||||||
|
$(eval MQ_IMAGE_ADVANCEDSERVER_PPC64LE_DIGEST=$(shell $(COMMAND) run skopeo:latest --override-os linux inspect --creds $(MQ_ARCHIVE_REPOSITORY_USER):$(MQ_ARCHIVE_REPOSITORY_CREDENTIAL) docker://$(MQ_IMAGE_ADVANCEDSERVER_PPC64LE) | jq -r .Digest))
|
||||||
$(info $(shell printf "** Determined the built $(MQ_IMAGE_ADVANCEDSERVER_AMD64) has a digest of $(MQ_IMAGE_ADVANCEDSERVER_AMD64_DIGEST)**"$(END)))
|
$(info $(shell printf "** Determined the built $(MQ_IMAGE_ADVANCEDSERVER_AMD64) has a digest of $(MQ_IMAGE_ADVANCEDSERVER_AMD64_DIGEST)**"$(END)))
|
||||||
$(info $(shell printf "** Determined the built $(MQ_IMAGE_ADVANCEDSERVER_S390X) has a digest of $(MQ_IMAGE_ADVANCEDSERVER_S390X_DIGEST)**"$(END)))
|
$(info $(shell printf "** Determined the built $(MQ_IMAGE_ADVANCEDSERVER_S390X) has a digest of $(MQ_IMAGE_ADVANCEDSERVER_S390X_DIGEST)**"$(END)))
|
||||||
|
$(info $(shell printf "** Determined the built $(MQ_IMAGE_ADVANCEDSERVER_PPC64LE) has a digest of $(MQ_IMAGE_ADVANCEDSERVER_PPC64LE_DIGEST)**"$(END)))
|
||||||
ifneq "$(LTS)" "true"
|
ifneq "$(LTS)" "true"
|
||||||
$(info $(shell printf "** Calling script to create fat-manifest for $(MQ_IMAGE_DEVSERVER_MANIFEST)**"$(END)))
|
$(info $(shell printf "** Calling script to create fat-manifest for $(MQ_IMAGE_DEVSERVER_MANIFEST)**"$(END)))
|
||||||
echo $(shell ./travis-build-scripts/create-manifest-list.sh -r $(MQ_DELIVERY_REGISTRY_HOSTNAME) -n $(MQ_DELIVERY_REGISTRY_NAMESPACE) -i $(MQ_IMAGE_DEVSERVER) -t $(MQ_MANIFEST_TAG) -u $(MQ_ARCHIVE_REPOSITORY_USER) -p $(MQ_ARCHIVE_REPOSITORY_CREDENTIAL) -d "$(MQ_IMAGE_DEVSERVER_AMD64_DIGEST) $(MQ_IMAGE_DEVSERVER_S390X_DIGEST)" $(END))
|
echo $(shell ./travis-build-scripts/create-manifest-list.sh -r $(MQ_DELIVERY_REGISTRY_HOSTNAME) -n $(MQ_DELIVERY_REGISTRY_NAMESPACE) -i $(MQ_IMAGE_DEVSERVER) -t $(MQ_MANIFEST_TAG) -u $(MQ_ARCHIVE_REPOSITORY_USER) -p $(MQ_ARCHIVE_REPOSITORY_CREDENTIAL) -d "$(MQ_IMAGE_DEVSERVER_AMD64_DIGEST) $(MQ_IMAGE_DEVSERVER_S390X_DIGEST) $(MQ_IMAGE_DEVSERVER_PPC64LE_DIGEST)" $(END))
|
||||||
endif
|
endif
|
||||||
$(info $(shell printf "** Calling script to create fat-manifest for $(MQ_IMAGE_ADVANCEDSERVER_MANIFEST)**"$(END)))
|
$(info $(shell printf "** Calling script to create fat-manifest for $(MQ_IMAGE_ADVANCEDSERVER_MANIFEST)**"$(END)))
|
||||||
echo $(shell ./travis-build-scripts/create-manifest-list.sh -r $(MQ_DELIVERY_REGISTRY_HOSTNAME) -n $(MQ_DELIVERY_REGISTRY_NAMESPACE) -i $(MQ_IMAGE_ADVANCEDSERVER) -t $(MQ_MANIFEST_TAG) -u $(MQ_ARCHIVE_REPOSITORY_USER) -p $(MQ_ARCHIVE_REPOSITORY_CREDENTIAL) -d "$(MQ_IMAGE_ADVANCEDSERVER_AMD64_DIGEST) $(MQ_IMAGE_ADVANCEDSERVER_S390X_DIGEST)" $(END))
|
echo $(shell ./travis-build-scripts/create-manifest-list.sh -r $(MQ_DELIVERY_REGISTRY_HOSTNAME) -n $(MQ_DELIVERY_REGISTRY_NAMESPACE) -i $(MQ_IMAGE_ADVANCEDSERVER) -t $(MQ_MANIFEST_TAG) -u $(MQ_ARCHIVE_REPOSITORY_USER) -p $(MQ_ARCHIVE_REPOSITORY_CREDENTIAL) -d "$(MQ_IMAGE_ADVANCEDSERVER_AMD64_DIGEST) $(MQ_IMAGE_ADVANCEDSERVER_S390X_DIGEST) $(MQ_IMAGE_ADVANCEDSERVER_PPC64LE_DIGEST)" $(END))
|
||||||
|
|
||||||
.PHONY: build-skopeo-container
|
.PHONY: build-skopeo-container
|
||||||
build-skopeo-container:
|
build-skopeo-container:
|
||||||
$(COMMAND) images | grep -q "skopeo"; if [ $$? != 0 ]; then docker build -t skopeo:latest ./docker-builds/skopeo/; fi
|
$(COMMAND) images | grep -q "skopeo"; if [ $$? != 0 ]; then $(COMMAND) build -t skopeo:latest ./docker-builds/skopeo/; fi
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Other targets
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
clean:
|
clean:
|
||||||
@@ -487,35 +533,20 @@ lint: $(addsuffix /$(wildcard *.go), $(GO_PKG_DIRS))
|
|||||||
.PHONY: gosec
|
.PHONY: gosec
|
||||||
gosec:
|
gosec:
|
||||||
$(info $(SPACER)$(shell printf "Running gosec test"$(END)))
|
$(info $(SPACER)$(shell printf "Running gosec test"$(END)))
|
||||||
@gosec -fmt=json -out=gosec_results.json cmd/... internal/... 2> /dev/null ;\
|
@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" ;\
|
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" ;\
|
|
||||||
exit 1 ;\
|
exit 1 ;\
|
||||||
else \
|
else \
|
||||||
printf "\ngosec found no HIGH severity issues\n" ;\
|
printf "gosec found no issues\n" ;\
|
||||||
fi ;\
|
cat "gosec_results.json" ;\
|
||||||
cat gosec_results.json | grep MEDIUM | grep severity > /dev/null ;\
|
fi
|
||||||
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 ;\
|
|
||||||
|
|
||||||
include formatting.mk
|
|
||||||
|
|
||||||
.PHONY: update-release-information
|
.PHONY: update-release-information
|
||||||
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))
|
$(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_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
|
sed -i.bak 's/ibm-mqadvanced-server:.*-amd64/ibm-mqadvanced-server:$(MQ_VERSION)-amd64/g' docs/security.md
|
||||||
@@ -525,3 +556,17 @@ update-release-information:
|
|||||||
sed -i.bak 's/knowledgecenter\/SSFKSJ_.*\/com/knowledgecenter\/SSFKSJ_${MQ_VERSION_2}.0\/com/g' docs/usage.md && rm docs/usage.md.bak
|
sed -i.bak 's/knowledgecenter\/SSFKSJ_.*\/com/knowledgecenter\/SSFKSJ_${MQ_VERSION_2}.0\/com/g' docs/usage.md && rm docs/usage.md.bak
|
||||||
$(eval MQ_VERSION_3=$(shell echo '${MQ_VERSION_1}' | sed "s/\.//g"))
|
$(eval MQ_VERSION_3=$(shell echo '${MQ_VERSION_1}' | sed "s/\.//g"))
|
||||||
sed -i.bak 's/MQ_..._ARCHIVE_REPOSITORY/MQ_${MQ_VERSION_3}_ARCHIVE_REPOSITORY/g' .travis.yml && rm .travis.yml.bak
|
sed -i.bak 's/MQ_..._ARCHIVE_REPOSITORY/MQ_${MQ_VERSION_3}_ARCHIVE_REPOSITORY/g' .travis.yml && rm .travis.yml.bak
|
||||||
|
|
||||||
|
COMMAND_SERVER_VERSION=$(shell $(COMMAND) version --format "{{ .Server.Version }}")
|
||||||
|
COMMAND_CLIENT_VERSION=$(shell $(COMMAND) version --format "{{ .Client.Version }}")
|
||||||
|
PODMAN_VERSION=$(shell podman version --format "{{ .Version }}")
|
||||||
|
.PHONY: command-version
|
||||||
|
command-version:
|
||||||
|
# If we're using Docker, then check it's recent enough to support multi-stage builds
|
||||||
|
ifneq (,$(findstring docker,$(COMMAND)))
|
||||||
|
@test "$(word 1,$(subst ., ,$(COMMAND_CLIENT_VERSION)))" -ge "17" || ("$(word 1,$(subst ., ,$(COMMAND_CLIENT_VERSION)))" -eq "17" && "$(word 2,$(subst ., ,$(COMMAND_CLIENT_VERSION)))" -ge "05") || (echo "Error: Docker client 17.05 or greater is required" && exit 1)
|
||||||
|
@test "$(word 1,$(subst ., ,$(COMMAND_SERVER_VERSION)))" -ge "17" || ("$(word 1,$(subst ., ,$(COMMAND_SERVER_VERSION)))" -eq "17" && "$(word 2,$(subst ., ,$(COMMAND_CLIENT_VERSION)))" -ge "05") || (echo "Error: Docker server 17.05 or greater is required" && exit 1)
|
||||||
|
endif
|
||||||
|
ifneq (,$(findstring podman,$(COMMAND)))
|
||||||
|
@test "$(word 1,$(subst ., ,$(PODMAN_VERSION)))" -ge "1" || (echo "Error: Podman version 1.0 or greater is required" && exit 1)
|
||||||
|
endif
|
||||||
17
README.md
17
README.md
@@ -1,5 +1,6 @@
|
|||||||
# IBM MQ container
|
# IBM MQ container
|
||||||
|
|
||||||
|
|
||||||
[](https://travis-ci.org/ibm-messaging/mq-container)
|
[](https://travis-ci.org/ibm-messaging/mq-container)
|
||||||
|
|
||||||
**Note**: The `master` branch may be in an *unstable or even broken state* during development.
|
**Note**: The `master` branch may be in an *unstable or even broken state* during development.
|
||||||
@@ -26,29 +27,33 @@ 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`.
|
- **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.
|
- **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.
|
- **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.
|
- **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.
|
See the [default developer configuration docs](docs/developer-config.md) for the extra environment variables supported by the MQ Advanced for Developers image.
|
||||||
|
|
||||||
### Kubernetes
|
### Kubernetes
|
||||||
|
|
||||||
If you want to use IBM MQ in [Kubernetes](https://kubernetes.io), you can find an example [Helm](https://helm.sh/) chart here: [IBM charts](https://github.com/IBM/charts). This can be used to run the container on a cluster, such as [IBM Cloud Private](https://www.ibm.com/cloud-computing/products/ibm-cloud-private/) or the [IBM Cloud Kubernetes Service](https://www.ibm.com/cloud/container-service).
|
If you want to use IBM MQ on [Kubernetes](https://kubernetes.io), you can find an example [Helm](https://helm.sh/) chart here: [IBM MQ Sample Helm Chart](https://github.com/ibm-messaging/mq-helm). This can be used to run the container on a Kubernetes cluster, such as the [IBM Cloud Kubernetes Service](https://www.ibm.com/cloud/container-service).
|
||||||
|
|
||||||
## Issues and contributions
|
## 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
|
## License
|
||||||
|
|
||||||
The Dockerfiles and associated code and scripts are licensed under the [Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0.html).
|
The Dockerfiles and associated code and scripts are licensed under the [Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0.html).
|
||||||
Licenses for the products installed within the images are as follows:
|
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-BMKG5H) (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 for Developers](http://www14.software.ibm.com/cgi-bin/weblap/lap.pl?la_formnum=Z125-3301-14&li_formnum=L-AXAF-JLZ53A) (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-BMJJBM) (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-AMRD-XH6P3Q) (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.
|
Note: The IBM MQ Advanced for Developers license does not permit further distribution and the terms restrict usage to a developer machine.
|
||||||
|
|
||||||
|
|
||||||
## Copyright
|
## Copyright
|
||||||
|
|
||||||
© Copyright IBM Corporation 2015, 2020
|
© Copyright IBM Corporation 2015, 2023
|
||||||
|
|||||||
62
authservice/mqhtpass/Makefile
Normal file
62
authservice/mqhtpass/Makefile
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
# © Copyright IBM Corporation 2017, 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.
|
||||||
|
|
||||||
|
# This Makefile expects the following to be installed:
|
||||||
|
# - gcc
|
||||||
|
# - ldd
|
||||||
|
# - MQ SDK (mqm_r library, plus header files)
|
||||||
|
# - Apache Portable Runtime (apr-1 and aprutil-1 libraries, plus header files)
|
||||||
|
|
||||||
|
SRC_DIR = src
|
||||||
|
BUILD_DIR = ./build
|
||||||
|
ARCH ?= $(if $(findstring x86_64,$(shell uname -m)),amd64,$(if $(findstring aarch64,$(shell uname -m)),aarch64,$(shell uname -m)))
|
||||||
|
|
||||||
|
# Flags passed to the C compiler. Need to use gnu11 to get POSIX functions needed for file locking.
|
||||||
|
CFLAGS.amd64 := -m64
|
||||||
|
CFLAGS.ppc64le := -m64
|
||||||
|
CFLAGS.s390x := -m64
|
||||||
|
# -m64 is not a valid compiler option on aarch64/arm64 (ARM)
|
||||||
|
CFLAGS.arm64 :=
|
||||||
|
CFLAGS += -std=gnu11 -fPIC -Wall ${CFLAGS.${ARCH}}
|
||||||
|
|
||||||
|
LIB_APR = -L/usr/lib64 -lapr-1 -laprutil-1
|
||||||
|
LIB_MQ = -L/opt/mqm/lib64 -lmqm_r
|
||||||
|
|
||||||
|
all: $(BUILD_DIR)/mqhtpass.so $(BUILD_DIR)/htpass_test $(BUILD_DIR)/log_test
|
||||||
|
|
||||||
|
$(BUILD_DIR)/log.o : $(SRC_DIR)/log.c $(SRC_DIR)/log.h
|
||||||
|
mkdir -p ${dir $@}
|
||||||
|
gcc $(CFLAGS) -c $(SRC_DIR)/log.c -o $@
|
||||||
|
|
||||||
|
$(BUILD_DIR)/log_test : $(BUILD_DIR)/log.o
|
||||||
|
mkdir -p ${dir $@}
|
||||||
|
gcc $(CFLAGS) $(SRC_DIR)/log_test.c $^ -o $@
|
||||||
|
# Run Logging tests, and print log if they fail
|
||||||
|
$@ || (cat log_test*.log && exit 1)
|
||||||
|
|
||||||
|
$(BUILD_DIR)/htpass.o : $(SRC_DIR)/htpass.c $(SRC_DIR)/htpass.h
|
||||||
|
mkdir -p ${dir $@}
|
||||||
|
gcc $(CFLAGS) -c $(SRC_DIR)/htpass.c -I /usr/include/apr-1 -o $@
|
||||||
|
|
||||||
|
$(BUILD_DIR)/htpass_test : $(BUILD_DIR)/htpass.o $(BUILD_DIR)/log.o
|
||||||
|
mkdir -p ${dir $@}
|
||||||
|
gcc $(CFLAGS) $(LIB_APR) -lpthread $(SRC_DIR)/htpass_test.c $^ -o $@
|
||||||
|
# Run HTPasswd tests, and print log if they fail
|
||||||
|
$@ || (cat htpass_test*.log && exit 1)
|
||||||
|
|
||||||
|
$(BUILD_DIR)/mqhtpass.so : $(BUILD_DIR)/log.o $(BUILD_DIR)/htpass.o
|
||||||
|
mkdir -p ${dir $@}
|
||||||
|
# NOTE: rpath for libapr will be different on Ubuntu
|
||||||
|
gcc $(CFLAGS) -I/opt/mqm/inc -D_REENTRANT $(LIB_APR) $(LIB_MQ) -Wl,-rpath,/opt/mqm/lib64 -Wl,-rpath,/usr/lib64 -shared $(SRC_DIR)/mqhtpass.c $^ -o $@
|
||||||
|
ldd $@
|
||||||
145
authservice/mqhtpass/src/htpass.c
Normal file
145
authservice/mqhtpass/src/htpass.c
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
/*
|
||||||
|
© Copyright IBM Corporation 2021
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "log.h"
|
||||||
|
#include "htpass.h"
|
||||||
|
#include <linux/limits.h>
|
||||||
|
#include <apr_general.h>
|
||||||
|
#include <apr_errno.h>
|
||||||
|
#include <apr_md5.h>
|
||||||
|
|
||||||
|
bool htpass_valid_file(char *filename)
|
||||||
|
{
|
||||||
|
bool valid = true;
|
||||||
|
FILE *fp;
|
||||||
|
char *huser;
|
||||||
|
|
||||||
|
fp = fopen(filename, "r");
|
||||||
|
if (fp == NULL)
|
||||||
|
{
|
||||||
|
log_errorf("Error %d opening htpasswd file '%s'", errno, filename);
|
||||||
|
}
|
||||||
|
if (fp)
|
||||||
|
{
|
||||||
|
const size_t line_size = 1024;
|
||||||
|
char *line = malloc(line_size);
|
||||||
|
while (fgets(line, line_size, fp) != NULL)
|
||||||
|
{
|
||||||
|
char *saveptr;
|
||||||
|
// Need to use strtok_r to be safe for multiple threads
|
||||||
|
huser = strtok_r(line, ":", &saveptr);
|
||||||
|
if (strlen(huser) >= 12)
|
||||||
|
{
|
||||||
|
log_errorf("Invalid htpasswd file for use with IBM MQ. User '%s' is longer than twelve characters", huser);
|
||||||
|
valid = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fclose(fp);
|
||||||
|
if (line)
|
||||||
|
{
|
||||||
|
free(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *find_hash(char *filename, char *user)
|
||||||
|
{
|
||||||
|
bool found = false;
|
||||||
|
FILE *fp;
|
||||||
|
char *huser;
|
||||||
|
char *hash;
|
||||||
|
|
||||||
|
fp = fopen(filename, "r");
|
||||||
|
if (fp == NULL)
|
||||||
|
{
|
||||||
|
log_errorf("Error %d opening htpasswd file '%s'", errno, filename);
|
||||||
|
}
|
||||||
|
if (fp)
|
||||||
|
{
|
||||||
|
const size_t line_size = 1024;
|
||||||
|
char *line = malloc(line_size);
|
||||||
|
while (fgets(line, line_size, fp) != NULL)
|
||||||
|
{
|
||||||
|
char *saveptr;
|
||||||
|
// Need to use strtok_r to be safe for multiple threads
|
||||||
|
huser = strtok_r(line, ":", &saveptr);
|
||||||
|
if (huser && (strcmp(user, huser) == 0))
|
||||||
|
{
|
||||||
|
// Make a duplicate of the string, because we'll be keeping it
|
||||||
|
hash = strdup(strtok_r(NULL, " \r\n\t", &saveptr));
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fclose(fp);
|
||||||
|
if (line)
|
||||||
|
{
|
||||||
|
free(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found)
|
||||||
|
{
|
||||||
|
hash = NULL;
|
||||||
|
}
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
int htpass_authenticate_user(char *filename, char *user, char *password)
|
||||||
|
{
|
||||||
|
char *hash = find_hash(filename, user);
|
||||||
|
int result = -1;
|
||||||
|
if (hash == NULL)
|
||||||
|
{
|
||||||
|
result = HTPASS_INVALID_USER;
|
||||||
|
log_debugf("User does not exist. user=%s", user);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Use the Apache Portable Runtime utilities to validate the password against the hash.
|
||||||
|
// Supports multiple hashing algorithms, but we should only be using bcrypt
|
||||||
|
apr_status_t status = apr_password_validate(password, hash);
|
||||||
|
// status is usually either APR_SUCCESS or APR_EMISMATCH
|
||||||
|
if (status == APR_SUCCESS)
|
||||||
|
{
|
||||||
|
result = HTPASS_VALID;
|
||||||
|
log_debugf("Correct password supplied. user=%s", user);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = HTPASS_INVALID_PASSWORD;
|
||||||
|
log_debugf("Incorrect password supplied. user=%s", user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool htpass_valid_user(char *filename, char *user)
|
||||||
|
{
|
||||||
|
char *hash = find_hash(filename, user);
|
||||||
|
bool valid = false;
|
||||||
|
if (hash != NULL)
|
||||||
|
{
|
||||||
|
valid = true;
|
||||||
|
}
|
||||||
|
return valid;
|
||||||
|
}
|
||||||
49
authservice/mqhtpass/src/htpass.h
Normal file
49
authservice/mqhtpass/src/htpass.h
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
© Copyright IBM Corporation 2021
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _HTPASS_H
|
||||||
|
#define _HTPASS_H
|
||||||
|
|
||||||
|
#define HTPASS_VALID 0
|
||||||
|
#define HTPASS_INVALID_USER 1
|
||||||
|
#define HTPASS_INVALID_PASSWORD 2
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate an HTPasswd file for use with IBM MQ.
|
||||||
|
*
|
||||||
|
* @param filename the HTPasswd file
|
||||||
|
*/
|
||||||
|
_Bool htpass_valid_file(char *filename);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Authenticate a user, based on the supplied file name.
|
||||||
|
*
|
||||||
|
* @param filename the HTPasswd file
|
||||||
|
* @param user the user name to authenticate
|
||||||
|
* @param password the password of the user
|
||||||
|
* @return HTPASS_VALID, HTPASS_INVALID_USER or HTPASS_INVALID_PASSWORD
|
||||||
|
*/
|
||||||
|
int htpass_authenticate_user(char *filename, char *user, char *password);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate that a user exists in the password file.
|
||||||
|
*
|
||||||
|
* @param filename the HTPasswd file
|
||||||
|
* @param user the user name to validate
|
||||||
|
*/
|
||||||
|
_Bool htpass_valid_user(char *filename, char *user);
|
||||||
|
|
||||||
|
#endif
|
||||||
223
authservice/mqhtpass/src/htpass_test.c
Normal file
223
authservice/mqhtpass/src/htpass_test.c
Normal file
@@ -0,0 +1,223 @@
|
|||||||
|
/*
|
||||||
|
© Copyright IBM Corporation 2021
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "log.h"
|
||||||
|
#include "htpass.h"
|
||||||
|
|
||||||
|
// Headers for multi-threaded tests
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
// Start a test and log the function name
|
||||||
|
#define test_start() printf("=== RUN: %s\n", __func__)
|
||||||
|
|
||||||
|
// Indicate test has passed
|
||||||
|
#define test_pass() printf("--- PASS: %s\n", __func__)
|
||||||
|
|
||||||
|
// Indicate test has failed
|
||||||
|
void test_fail(const char *test_name)
|
||||||
|
{
|
||||||
|
printf("--- FAIL: %s\n", test_name);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Simple tests for file validation
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void test_htpass_valid_file_ok()
|
||||||
|
{
|
||||||
|
test_start();
|
||||||
|
int ok = htpass_valid_file("./src/htpass_test.htpasswd");
|
||||||
|
if (!ok)
|
||||||
|
test_fail(__func__);
|
||||||
|
test_pass();
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_htpass_valid_file_too_long()
|
||||||
|
{
|
||||||
|
test_start();
|
||||||
|
int ok = htpass_valid_file("./src/htpass_test_invalid.htpasswd");
|
||||||
|
if (ok)
|
||||||
|
test_fail(__func__);
|
||||||
|
test_pass();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Simple tests for authentication
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void test_htpass_authenticate_user_fred_valid()
|
||||||
|
{
|
||||||
|
test_start();
|
||||||
|
int rc = htpass_authenticate_user("./src/htpass_test.htpasswd", "fred", "passw0rd");
|
||||||
|
printf("%s: fred - %d\n", __func__, rc);
|
||||||
|
if (rc != HTPASS_VALID)
|
||||||
|
test_fail(__func__);
|
||||||
|
test_pass();
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_htpass_authenticate_user_fred_invalid1()
|
||||||
|
{
|
||||||
|
test_start();
|
||||||
|
int rc = htpass_authenticate_user("./src/htpass_test.htpasswd", "fred", "passw0rd ");
|
||||||
|
printf("%s: fred - %d\n", __func__, rc);
|
||||||
|
if (rc != HTPASS_INVALID_PASSWORD)
|
||||||
|
test_fail(__func__);
|
||||||
|
test_pass();
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_htpass_authenticate_user_fred_invalid2()
|
||||||
|
{
|
||||||
|
test_start();
|
||||||
|
int rc = htpass_authenticate_user("./src/htpass_test.htpasswd", "fred", "");
|
||||||
|
printf("%s: fred - %d\n", __func__, rc);
|
||||||
|
if (rc != HTPASS_INVALID_PASSWORD)
|
||||||
|
test_fail(__func__);
|
||||||
|
test_pass();
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_htpass_authenticate_user_fred_invalid3()
|
||||||
|
{
|
||||||
|
test_start();
|
||||||
|
int rc = htpass_authenticate_user("./src/htpass_test.htpasswd", "fred", "clearlywrong");
|
||||||
|
printf("%s: fred - %d\n", __func__, rc);
|
||||||
|
if (rc != HTPASS_INVALID_PASSWORD)
|
||||||
|
test_fail(__func__);
|
||||||
|
test_pass();
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_htpass_authenticate_user_barney_valid()
|
||||||
|
{
|
||||||
|
test_start();
|
||||||
|
int rc = htpass_authenticate_user("./src/htpass_test.htpasswd", "barney", "s3cret");
|
||||||
|
printf("%s: barney - %d\n", __func__, rc);
|
||||||
|
if (rc != HTPASS_VALID)
|
||||||
|
test_fail(__func__);
|
||||||
|
test_pass();
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_htpass_authenticate_user_unknown()
|
||||||
|
{
|
||||||
|
test_start();
|
||||||
|
int rc = htpass_authenticate_user("./src/htpass_test.htpasswd", "george", "s3cret");
|
||||||
|
printf("%s: barney - %d\n", __func__, rc);
|
||||||
|
if (rc != HTPASS_INVALID_USER)
|
||||||
|
test_fail(__func__);
|
||||||
|
test_pass();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Multi-threaded test
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#define NUM_THREADS 5
|
||||||
|
// Number of tests to perform per thread. Higher numbers are more likely to trigger timing issue.
|
||||||
|
#define NUM_TESTS_PER_THREAD 1000
|
||||||
|
// Maximum number of JSON errors to report (log can get flooded)
|
||||||
|
#define MAX_JSON_ERRORS 10
|
||||||
|
|
||||||
|
// Authenticate multiple users, multiple times
|
||||||
|
void *authenticate_many_times(void *p)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < NUM_TESTS_PER_THREAD; i++)
|
||||||
|
{
|
||||||
|
int rc = htpass_authenticate_user("./src/htpass_test.htpasswd", "barney", "s3cret");
|
||||||
|
if (rc != HTPASS_VALID)
|
||||||
|
test_fail(__func__);
|
||||||
|
rc = htpass_authenticate_user("./src/htpass_test.htpasswd", "fred", "passw0rd");
|
||||||
|
if (rc != HTPASS_VALID)
|
||||||
|
test_fail(__func__);
|
||||||
|
}
|
||||||
|
pthread_exit(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void check_log_file_valid(char *filename)
|
||||||
|
{
|
||||||
|
int errors = 0;
|
||||||
|
printf("--- Checking log file is valid\n");
|
||||||
|
// Check that the JSON log file isn't corrupted
|
||||||
|
FILE *log = fopen(filename, "r");
|
||||||
|
if (log == NULL)
|
||||||
|
{
|
||||||
|
test_fail(__func__);
|
||||||
|
}
|
||||||
|
const size_t line_size = 1024;
|
||||||
|
char *line = malloc(line_size);
|
||||||
|
while (fgets(line, line_size, log) != NULL)
|
||||||
|
{
|
||||||
|
if ((line[0] != '{') && (errors < MAX_JSON_ERRORS))
|
||||||
|
{
|
||||||
|
printf("*** Invalid JSON detected: %s\n", line);
|
||||||
|
errors++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (line)
|
||||||
|
{
|
||||||
|
free(line);
|
||||||
|
}
|
||||||
|
fclose(log);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test authenticate_user with multiple threads, each doing many authentications
|
||||||
|
void test_htpass_authenticate_user_multithreaded(char *logfile)
|
||||||
|
{
|
||||||
|
pthread_t threads[NUM_THREADS];
|
||||||
|
int rc;
|
||||||
|
test_start();
|
||||||
|
// Re-initialize the log to use a file for the multi-threaded test
|
||||||
|
log_init(logfile);
|
||||||
|
for (int i = 0; i < NUM_THREADS; i++)
|
||||||
|
{
|
||||||
|
printf("Creating thread %d\n", i);
|
||||||
|
rc = pthread_create(&threads[i], NULL, authenticate_many_times, NULL);
|
||||||
|
if (rc)
|
||||||
|
{
|
||||||
|
printf("Error: Unable to create thread, %d\n", rc);
|
||||||
|
test_fail(__func__);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Wait for all the threads to complete
|
||||||
|
for (int i = 0; i < NUM_THREADS; i++)
|
||||||
|
{
|
||||||
|
pthread_join(threads[i], NULL);
|
||||||
|
}
|
||||||
|
check_log_file_valid(logfile);
|
||||||
|
test_pass();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
// Turn on debugging for the tests
|
||||||
|
setenv("DEBUG", "true", true);
|
||||||
|
log_init("htpass_test.log");
|
||||||
|
test_htpass_valid_file_ok();
|
||||||
|
test_htpass_valid_file_too_long();
|
||||||
|
test_htpass_authenticate_user_fred_valid();
|
||||||
|
test_htpass_authenticate_user_fred_invalid1();
|
||||||
|
test_htpass_authenticate_user_fred_invalid2();
|
||||||
|
test_htpass_authenticate_user_fred_invalid3();
|
||||||
|
test_htpass_authenticate_user_barney_valid();
|
||||||
|
test_htpass_authenticate_user_unknown();
|
||||||
|
log_close();
|
||||||
|
|
||||||
|
// Call multi-threaded test last, because it re-initializes the log to use a file
|
||||||
|
test_htpass_authenticate_user_multithreaded("htpass_test_multithreaded.log");
|
||||||
|
}
|
||||||
2
authservice/mqhtpass/src/htpass_test.htpasswd
Normal file
2
authservice/mqhtpass/src/htpass_test.htpasswd
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
fred:$2y$05$3Fp9epsqEwWOHdyj9Ngf9.qfX34kzc9zNrdQ7kac0GmcCvQjIkAwy
|
||||||
|
barney:$2y$05$l8EoyCQ9y2PyfUzIDDfTyu7SSaJEYB1TuHy07xZvN7xt/pR3SIw0a
|
||||||
3
authservice/mqhtpass/src/htpass_test_invalid.htpasswd
Normal file
3
authservice/mqhtpass/src/htpass_test_invalid.htpasswd
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
fred:$2y$05$3Fp9epsqEwWOHdyj9Ngf9.qfX34kzc9zNrdQ7kac0GmcCvQjIkAwy
|
||||||
|
barney:$2y$05$l8EoyCQ9y2PyfUzIDDfTyu7SSaJEYB1TuHy07xZvN7xt/pR3SIw0a
|
||||||
|
namewhichisfartoolongformq:$2y$05$l8EoyCQ9y2PyfUzIDDfTyu7SSaJEYB1TuHy07xZvN7xt/pR3SIw0a
|
||||||
162
authservice/mqhtpass/src/log.c
Normal file
162
authservice/mqhtpass/src/log.c
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
/*
|
||||||
|
© Copyright IBM Corporation 2021, 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
FILE *fp = NULL;
|
||||||
|
int pid;
|
||||||
|
char hostname[255];
|
||||||
|
bool debug = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether debugging is enabled or not, using an environment variable.
|
||||||
|
*/
|
||||||
|
void init_debug(){
|
||||||
|
char *debug_env = getenv("DEBUG");
|
||||||
|
if (debug_env != NULL)
|
||||||
|
{
|
||||||
|
// Enable debug logging if the DEBUG environment variable is set
|
||||||
|
if (strncmp(debug_env, "true", 4) || strncmp(debug_env, "1", 1))
|
||||||
|
{
|
||||||
|
debug = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal function to initialize the log with the given file mode.
|
||||||
|
*/
|
||||||
|
int log_init_internal(char *filename, const char *mode)
|
||||||
|
{
|
||||||
|
int result = 0;
|
||||||
|
pid = getpid();
|
||||||
|
hostname[254] = '\0';
|
||||||
|
gethostname(hostname, 254);
|
||||||
|
if (!fp)
|
||||||
|
{
|
||||||
|
fp = fopen(filename, "a");
|
||||||
|
if (fp)
|
||||||
|
{
|
||||||
|
// Disable buffering for this file
|
||||||
|
setbuf(fp, NULL);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
init_debug();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int log_init_reset(char *filename)
|
||||||
|
{
|
||||||
|
// Open the log file for writing (overwrite if it already exists)
|
||||||
|
return log_init_internal(filename, "w");
|
||||||
|
}
|
||||||
|
|
||||||
|
int log_init(char *filename)
|
||||||
|
{
|
||||||
|
// Open the log file file for appending
|
||||||
|
return log_init_internal(filename, "a");
|
||||||
|
}
|
||||||
|
|
||||||
|
void log_init_file(FILE *f)
|
||||||
|
{
|
||||||
|
fp = f;
|
||||||
|
init_debug();
|
||||||
|
}
|
||||||
|
|
||||||
|
void log_close()
|
||||||
|
{
|
||||||
|
if (fp)
|
||||||
|
{
|
||||||
|
fclose(fp);
|
||||||
|
fp = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void log_printf(const char *source_file, int source_line, const char *level, const char *format, ...)
|
||||||
|
{
|
||||||
|
if (fp)
|
||||||
|
{
|
||||||
|
// If this is a DEBUG message, and debugging is off
|
||||||
|
if ((strncmp(level, "DEBUG", 5) == 0) && !debug)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
char buf[1024] = "";
|
||||||
|
char *cur = buf;
|
||||||
|
char* const end = buf + sizeof buf;
|
||||||
|
char date_buf[70];
|
||||||
|
struct tm *utc;
|
||||||
|
time_t t;
|
||||||
|
struct timeval now;
|
||||||
|
|
||||||
|
gettimeofday(&now, NULL);
|
||||||
|
t = now.tv_sec;
|
||||||
|
t = time(NULL);
|
||||||
|
utc = gmtime(&t);
|
||||||
|
|
||||||
|
cur += snprintf(cur, end-cur, "{");
|
||||||
|
cur += snprintf(cur, end-cur, "\"loglevel\":\"%s\"", level);
|
||||||
|
// Print ISO-8601 time and date
|
||||||
|
if (strftime(date_buf, sizeof date_buf, "%FT%T", utc))
|
||||||
|
{
|
||||||
|
// Round microseconds down to milliseconds, for consistency
|
||||||
|
cur += snprintf(cur, end-cur, ", \"ibm_datetime\":\"%s.%03ldZ\"", date_buf, now.tv_usec / (long)1000);
|
||||||
|
}
|
||||||
|
cur += snprintf(cur, end-cur, ", \"ibm_processId\":\"%d\"", pid);
|
||||||
|
cur += snprintf(cur, end-cur, ", \"host\":\"%s\"", hostname);
|
||||||
|
cur += snprintf(cur, end-cur, ", \"module\":\"%s:%d\"", source_file, source_line);
|
||||||
|
cur += snprintf(cur, end-cur, ", \"message\":\"");
|
||||||
|
|
||||||
|
if (strncmp(level, "DEBUG", 5) == 0)
|
||||||
|
{
|
||||||
|
// Add a prefix on any debug messages
|
||||||
|
cur += snprintf(cur, end-cur, "mqhtpass: ");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print log message, using varargs
|
||||||
|
va_list args;
|
||||||
|
va_start(args, format);
|
||||||
|
cur += vsnprintf(cur, end-cur, format, args);
|
||||||
|
va_end(args);
|
||||||
|
cur += snprintf(cur, end-cur, "\"}\n");
|
||||||
|
|
||||||
|
// Important: Just do one file write, to prevent problems with multi-threading.
|
||||||
|
// This only works if the log message is not too long for the buffer.
|
||||||
|
fprintf(fp, "%s", buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int trimmed_len(char *s, int max_len)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = max_len - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
if (s[i] != ' ')
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return i+1;
|
||||||
|
}
|
||||||
70
authservice/mqhtpass/src/log.h
Normal file
70
authservice/mqhtpass/src/log.h
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
© Copyright IBM Corporation 2021, 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _LOG_H
|
||||||
|
#define _LOG_H
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the log to use the given file name, wiping any existing contents.
|
||||||
|
*/
|
||||||
|
int log_init_reset(char *filename);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the log to use the given file name.
|
||||||
|
*/
|
||||||
|
int log_init(char *filename);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the log with an existing file handle.
|
||||||
|
*/
|
||||||
|
void log_init_file(FILE *f);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write a message to the log file, based on a printf format string.
|
||||||
|
*
|
||||||
|
* @param source_file the name of the source code file submitting this log message
|
||||||
|
* @param source_line the line of code in the source file
|
||||||
|
* @param level the log level, one of "DEBUG", "INFO" or "ERROR"
|
||||||
|
* @param format the printf format string for the message
|
||||||
|
*/
|
||||||
|
void log_printf(const char *source_file, int source_line, const char *level, const char *format, ...);
|
||||||
|
|
||||||
|
void log_close();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Variadic macro to write an informational message to the log file, based on a printf format string.
|
||||||
|
*/
|
||||||
|
#define log_infof(format,...) log_printf(__FILE__, __LINE__, "INFO", format, ##__VA_ARGS__)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Variadic macro to write an error message to the log file, based on a printf format string.
|
||||||
|
*/
|
||||||
|
#define log_errorf(format,...) log_printf(__FILE__, __LINE__, "ERROR", format, ##__VA_ARGS__)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Variadic macro to write a debug message to the log file, based on a printf format string.
|
||||||
|
*/
|
||||||
|
#define log_debugf(format,...) log_printf(__FILE__, __LINE__, "DEBUG", format, ##__VA_ARGS__)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the length of the string when trimmed of trailing spaces.
|
||||||
|
* IBM MQ uses fixed length strings, so this function can be used to print
|
||||||
|
* a trimmed version of a string using the "%.*s" printf format string.
|
||||||
|
* For example, `log_printf("%.*s", trimmed_len(fw_str, 48), fw_str)`
|
||||||
|
*/
|
||||||
|
int trimmed_len(char *s, int);
|
||||||
|
|
||||||
|
#endif
|
||||||
120
authservice/mqhtpass/src/log_test.c
Normal file
120
authservice/mqhtpass/src/log_test.c
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
/*
|
||||||
|
© 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "log.h"
|
||||||
|
|
||||||
|
// Headers for multi-threaded tests
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
// Start a test and log the function name
|
||||||
|
#define test_start() printf("=== RUN: %s\n", __func__)
|
||||||
|
|
||||||
|
// Indicate test has passed
|
||||||
|
#define test_pass() printf("--- PASS: %s\n", __func__)
|
||||||
|
|
||||||
|
// The length of strings used in the tests
|
||||||
|
#define STR_LEN 5
|
||||||
|
|
||||||
|
// Indicate test has failed
|
||||||
|
void test_fail(const char *test_name)
|
||||||
|
{
|
||||||
|
printf("--- FAIL: %s\n", test_name);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print a fixed-width string in hexadecimal
|
||||||
|
void print_hex(char fw_string[STR_LEN])
|
||||||
|
{
|
||||||
|
printf("[");
|
||||||
|
for (int i=0; i<STR_LEN; i++)
|
||||||
|
{
|
||||||
|
printf("%02x", fw_string[i]);
|
||||||
|
if (i < STR_LEN-1)
|
||||||
|
printf(",");
|
||||||
|
}
|
||||||
|
printf("]");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Tests for string manipulation
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void test_trimmed_len(const char *test_name, char fw_string[STR_LEN], int expected_len)
|
||||||
|
{
|
||||||
|
printf("=== RUN: %s\n", test_name);
|
||||||
|
int len;
|
||||||
|
// Create a copy of the fixed-width string
|
||||||
|
char fw_string2[STR_LEN];
|
||||||
|
memcpy(fw_string2, fw_string, STR_LEN * sizeof(char));
|
||||||
|
// Call the function under test
|
||||||
|
len = trimmed_len(fw_string, STR_LEN);
|
||||||
|
// Check the result is correct
|
||||||
|
if (len != expected_len)
|
||||||
|
{
|
||||||
|
printf("%s: Expected result to be %d; got %d\n", __func__, expected_len, len);
|
||||||
|
test_fail(test_name);
|
||||||
|
}
|
||||||
|
// Check that the original string has not been changed
|
||||||
|
for (int i=0; i<STR_LEN; i++)
|
||||||
|
{
|
||||||
|
if (fw_string[i] != fw_string2[i])
|
||||||
|
{
|
||||||
|
printf("%c-%c\n", fw_string[i], fw_string2[i]);
|
||||||
|
printf("%s: Expected string to be identical to input hex ", __func__);
|
||||||
|
print_hex(fw_string2);
|
||||||
|
printf("; got hex ");
|
||||||
|
print_hex(fw_string);
|
||||||
|
printf("\n");
|
||||||
|
test_fail(test_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("--- PASS: %s\n", test_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_trimmed_len_normal()
|
||||||
|
{
|
||||||
|
char fw_string[STR_LEN] = {'a','b','c',' ',' '};
|
||||||
|
test_trimmed_len(__func__, fw_string, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_trimmed_len_full()
|
||||||
|
{
|
||||||
|
char fw_string[STR_LEN] = {'a','b','c','d','e'};
|
||||||
|
test_trimmed_len(__func__, fw_string, 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_trimmed_len_empty()
|
||||||
|
{
|
||||||
|
char fw_string[STR_LEN] = {' ',' ',' ',' ',' '};
|
||||||
|
test_trimmed_len(__func__, fw_string, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
// Turn on debugging for the tests
|
||||||
|
setenv("DEBUG", "true", true);
|
||||||
|
log_init("log_test.log");
|
||||||
|
test_trimmed_len_normal();
|
||||||
|
test_trimmed_len_full();
|
||||||
|
test_trimmed_len_empty();
|
||||||
|
log_close();
|
||||||
|
}
|
||||||
342
authservice/mqhtpass/src/mqhtpass.c
Normal file
342
authservice/mqhtpass/src/mqhtpass.c
Normal file
@@ -0,0 +1,342 @@
|
|||||||
|
/*
|
||||||
|
© Copyright IBM Corporation 2021, 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// This is a developer only configuration and not recommended for production usage.
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <cmqec.h>
|
||||||
|
#include "log.h"
|
||||||
|
#include "htpass.h"
|
||||||
|
|
||||||
|
// Declare the internal functions that implement the interface
|
||||||
|
MQZ_INIT_AUTHORITY MQStart;
|
||||||
|
static MQZ_AUTHENTICATE_USER mqhtpass_authenticate_user;
|
||||||
|
static MQZ_FREE_USER mqhtpass_free_user;
|
||||||
|
static MQZ_TERM_AUTHORITY mqhtpass_terminate;
|
||||||
|
|
||||||
|
#define LOG_FILE "/var/mqm/errors/mqhtpass.json"
|
||||||
|
#define HTPASSWD_FILE "/etc/mqm/mq.htpasswd"
|
||||||
|
#define NAME "MQ Advanced for Developers custom authentication service"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialization and entrypoint for the dynamically loaded
|
||||||
|
* authorization installable service. It registers the addresses of the
|
||||||
|
* other functions which are to be called by the queue manager.
|
||||||
|
*
|
||||||
|
* This function is called whenever the module is loaded. The Options
|
||||||
|
* field will show whether it's a PRIMARY (i.e. during qmgr startup) or
|
||||||
|
* SECONDARY.
|
||||||
|
*/
|
||||||
|
void MQENTRY MQStart(
|
||||||
|
MQHCONFIG hc,
|
||||||
|
MQLONG Options,
|
||||||
|
MQCHAR48 QMgrName,
|
||||||
|
MQLONG ComponentDataLength,
|
||||||
|
PMQBYTE ComponentData,
|
||||||
|
PMQLONG Version,
|
||||||
|
PMQLONG pCompCode,
|
||||||
|
PMQLONG pReason)
|
||||||
|
{
|
||||||
|
MQLONG CC = MQCC_OK;
|
||||||
|
MQLONG Reason = MQRC_NONE;
|
||||||
|
int log_rc = 0;
|
||||||
|
|
||||||
|
if (Options == MQZIO_PRIMARY)
|
||||||
|
{
|
||||||
|
// Reset the log file. The file could still get large if debug is turned on,
|
||||||
|
// but this is a simpler solution for now.
|
||||||
|
log_rc = log_init_reset(LOG_FILE);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
log_rc = log_init(LOG_FILE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (log_rc != 0)
|
||||||
|
{
|
||||||
|
CC = MQCC_FAILED;
|
||||||
|
Reason = MQRC_INITIALIZATION_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Options == MQZIO_PRIMARY)
|
||||||
|
{
|
||||||
|
log_infof("Initializing %s", NAME);
|
||||||
|
}
|
||||||
|
log_debugf("MQStart options=%s qmgr=%.*s", ((Options == MQZIO_SECONDARY) ? "Secondary" : "Primary"), trimmed_len(QMgrName, MQ_Q_MGR_NAME_LENGTH), QMgrName);
|
||||||
|
|
||||||
|
if (!htpass_valid_file(HTPASSWD_FILE))
|
||||||
|
{
|
||||||
|
CC = MQCC_FAILED;
|
||||||
|
Reason = MQRC_INITIALIZATION_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the functions to use for each entry point
|
||||||
|
if (CC == MQCC_OK)
|
||||||
|
{
|
||||||
|
hc->MQZEP_Call(hc, MQZID_INIT_AUTHORITY, (PMQFUNC)MQStart, &CC, &Reason);
|
||||||
|
}
|
||||||
|
if (CC == MQCC_OK)
|
||||||
|
{
|
||||||
|
hc->MQZEP_Call(hc, MQZID_TERM_AUTHORITY, (PMQFUNC)mqhtpass_terminate, &CC, &Reason);
|
||||||
|
}
|
||||||
|
if (CC == MQCC_OK)
|
||||||
|
{
|
||||||
|
hc->MQZEP_Call(hc, MQZID_AUTHENTICATE_USER, (PMQFUNC)mqhtpass_authenticate_user, &CC, &Reason);
|
||||||
|
}
|
||||||
|
if (CC == MQCC_OK)
|
||||||
|
{
|
||||||
|
hc->MQZEP_Call(hc, MQZID_FREE_USER, (PMQFUNC)mqhtpass_free_user, &CC, &Reason);
|
||||||
|
}
|
||||||
|
*Version = MQZAS_VERSION_5;
|
||||||
|
*pCompCode = CC;
|
||||||
|
*pReason = Reason;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called during the connection of any application which supplies an MQCSP (Connection Security Parameters).
|
||||||
|
* This is the usual case.
|
||||||
|
* See https://www.ibm.com/support/knowledgecenter/SSFKSJ_latest/com.ibm.mq.ref.dev.doc/q095610_.html
|
||||||
|
*/
|
||||||
|
static void MQENTRY mqhtpass_authenticate_user_csp(
|
||||||
|
PMQCHAR pQMgrName,
|
||||||
|
PMQCSP pSecurityParms,
|
||||||
|
PMQZAC pApplicationContext,
|
||||||
|
PMQZIC pIdentityContext,
|
||||||
|
PMQPTR pCorrelationPtr,
|
||||||
|
PMQBYTE pComponentData,
|
||||||
|
PMQLONG pContinuation,
|
||||||
|
PMQLONG pCompCode,
|
||||||
|
PMQLONG pReason)
|
||||||
|
{
|
||||||
|
char *csp_user = NULL;
|
||||||
|
char *csp_pass = NULL;
|
||||||
|
|
||||||
|
// Firstly, create null-terminated strings from the user credentials in the MQ CSP object
|
||||||
|
csp_user = malloc(pSecurityParms->CSPUserIdLength + 1);
|
||||||
|
if (!csp_user)
|
||||||
|
{
|
||||||
|
log_errorf("%s is unable to allocate memory for a user", NAME);
|
||||||
|
*pCompCode = MQCC_FAILED;
|
||||||
|
*pReason = MQRC_SERVICE_ERROR;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
strncpy(csp_user, pSecurityParms->CSPUserIdPtr, pSecurityParms->CSPUserIdLength);
|
||||||
|
csp_user[pSecurityParms->CSPUserIdLength] = 0;
|
||||||
|
csp_pass = malloc((pSecurityParms->CSPPasswordLength + 1));
|
||||||
|
if (!csp_pass)
|
||||||
|
{
|
||||||
|
log_errorf("%s is unable to allocate memory for a password", NAME);
|
||||||
|
*pCompCode = MQCC_FAILED;
|
||||||
|
*pReason = MQRC_SERVICE_ERROR;
|
||||||
|
if (csp_user)
|
||||||
|
{
|
||||||
|
free(csp_user);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
strncpy(csp_pass, pSecurityParms->CSPPasswordPtr, pSecurityParms->CSPPasswordLength);
|
||||||
|
csp_pass[pSecurityParms->CSPPasswordLength] = 0;
|
||||||
|
log_debugf("%s with CSP user set. user=%s", __func__, csp_user);
|
||||||
|
int auth_result = htpass_authenticate_user(HTPASSWD_FILE, csp_user, csp_pass);
|
||||||
|
|
||||||
|
if (auth_result == HTPASS_VALID)
|
||||||
|
{
|
||||||
|
// An OK completion code means MQ will accept this user is authenticated
|
||||||
|
*pCompCode = MQCC_OK;
|
||||||
|
*pReason = MQRC_NONE;
|
||||||
|
// Tell the queue manager to stop trying other authorization services.
|
||||||
|
*pContinuation = MQZCI_STOP;
|
||||||
|
memcpy(pIdentityContext->UserIdentifier, csp_user, sizeof(pIdentityContext->UserIdentifier));
|
||||||
|
log_debugf("Authenticated user=%s", pIdentityContext->UserIdentifier);
|
||||||
|
}
|
||||||
|
// If the htpasswd file does not have an entry for this user
|
||||||
|
else if (auth_result == HTPASS_INVALID_USER)
|
||||||
|
{
|
||||||
|
*pCompCode = MQCC_WARNING;
|
||||||
|
*pReason = MQRC_NONE;
|
||||||
|
// Tell the queue manager to continue trying other authorization services, as they might have the user.
|
||||||
|
*pContinuation = MQZCI_CONTINUE;
|
||||||
|
log_debugf(
|
||||||
|
"User authentication failed due to invalid user. user=%.*s effuser=%.*s applname=%.*s csp_user=%s cc=%d reason=%d",
|
||||||
|
trimmed_len(pIdentityContext->UserIdentifier, MQ_USER_ID_LENGTH),
|
||||||
|
pIdentityContext->UserIdentifier,
|
||||||
|
trimmed_len(pApplicationContext->EffectiveUserID, MQ_USER_ID_LENGTH),
|
||||||
|
pApplicationContext->EffectiveUserID,
|
||||||
|
trimmed_len(pApplicationContext->ApplName, MQ_APPL_NAME_LENGTH),
|
||||||
|
pApplicationContext->ApplName,
|
||||||
|
csp_user,
|
||||||
|
*pCompCode,
|
||||||
|
*pReason);
|
||||||
|
}
|
||||||
|
// If the htpasswd file has an entry for this user, but the password supplied is incorrect
|
||||||
|
else if (auth_result == HTPASS_INVALID_PASSWORD)
|
||||||
|
{
|
||||||
|
*pCompCode = MQCC_WARNING;
|
||||||
|
*pReason = MQRC_NOT_AUTHORIZED;
|
||||||
|
// Tell the queue manager to stop trying other authorization services.
|
||||||
|
*pContinuation = MQZCI_STOP;
|
||||||
|
log_debugf(
|
||||||
|
"User authentication failed due to invalid password. user=%.*s effuser=%.*s applname=%.*s csp_user=%s cc=%d reason=%d",
|
||||||
|
trimmed_len(pIdentityContext->UserIdentifier, MQ_USER_ID_LENGTH),
|
||||||
|
pIdentityContext->UserIdentifier,
|
||||||
|
trimmed_len(pApplicationContext->EffectiveUserID, MQ_USER_ID_LENGTH),
|
||||||
|
pApplicationContext->EffectiveUserID,
|
||||||
|
trimmed_len(pApplicationContext->ApplName, MQ_APPL_NAME_LENGTH),
|
||||||
|
pApplicationContext->ApplName,
|
||||||
|
csp_user,
|
||||||
|
*pCompCode,
|
||||||
|
*pReason);
|
||||||
|
}
|
||||||
|
if (csp_user)
|
||||||
|
{
|
||||||
|
free(csp_user);
|
||||||
|
}
|
||||||
|
if (csp_pass)
|
||||||
|
{
|
||||||
|
free(csp_pass);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called during the connection of any application.
|
||||||
|
* For more information on the parameters, see https://www.ibm.com/support/knowledgecenter/en/SSFKSJ_latest/com.ibm.mq.ref.dev.doc/q110090_.html
|
||||||
|
*/
|
||||||
|
static void MQENTRY mqhtpass_authenticate_user(
|
||||||
|
PMQCHAR pQMgrName,
|
||||||
|
PMQCSP pSecurityParms,
|
||||||
|
PMQZAC pApplicationContext,
|
||||||
|
PMQZIC pIdentityContext,
|
||||||
|
PMQPTR pCorrelationPtr,
|
||||||
|
PMQBYTE pComponentData,
|
||||||
|
PMQLONG pContinuation,
|
||||||
|
PMQLONG pCompCode,
|
||||||
|
PMQLONG pReason)
|
||||||
|
{
|
||||||
|
char *spuser = NULL;
|
||||||
|
// By default, return a warning, which indicates to MQ that this
|
||||||
|
// authorization service hasn't authenticated the user.
|
||||||
|
*pCompCode = MQCC_WARNING;
|
||||||
|
*pReason = MQRC_NONE;
|
||||||
|
// By default, tell the queue manager to continue trying other
|
||||||
|
// authorization services.
|
||||||
|
*pContinuation = MQZCI_CONTINUE;
|
||||||
|
|
||||||
|
if ((pSecurityParms->AuthenticationType) == MQCSP_AUTH_USER_ID_AND_PWD)
|
||||||
|
{
|
||||||
|
mqhtpass_authenticate_user_csp(pQMgrName, pSecurityParms, pApplicationContext, pIdentityContext, pCorrelationPtr, pComponentData, pContinuation, pCompCode, pReason);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Password not supplied, so just check that the user ID is valid
|
||||||
|
spuser = malloc(sizeof(PMQCHAR12) + 1);
|
||||||
|
if (!spuser)
|
||||||
|
{
|
||||||
|
log_errorf("%s is unable to allocate memory to check a user", NAME);
|
||||||
|
*pCompCode = MQCC_FAILED;
|
||||||
|
*pReason = MQRC_SERVICE_ERROR;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
strncpy(spuser, pApplicationContext->EffectiveUserID, strlen(pApplicationContext->EffectiveUserID));
|
||||||
|
spuser[sizeof(PMQCHAR12)] = 0;
|
||||||
|
log_debugf("%s without CSP user set. effectiveuid=%s env=%d, callertype=%d, type=%d, accttoken=%d applidentitydata=%d", __func__, spuser, pApplicationContext->Environment, pApplicationContext->CallerType, pApplicationContext->AuthenticationType, pIdentityContext->AccountingToken, pIdentityContext->ApplIdentityData);
|
||||||
|
if (strncmp(spuser, "mqm", 3) == 0)
|
||||||
|
{
|
||||||
|
// Special case: pass the "mqm" user on for validation up the chain
|
||||||
|
// A warning in the completion code means MQ will pass this to other authorization services
|
||||||
|
*pCompCode = MQCC_WARNING;
|
||||||
|
*pReason = MQRC_NONE;
|
||||||
|
*pContinuation = MQZCI_CONTINUE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bool valid_user = htpass_valid_user(HTPASSWD_FILE, spuser);
|
||||||
|
if (valid_user)
|
||||||
|
{
|
||||||
|
// An OK completion code means MQ will accept this user is authenticated
|
||||||
|
*pCompCode = MQCC_OK;
|
||||||
|
*pReason = MQRC_NONE;
|
||||||
|
*pContinuation = MQZCI_STOP;
|
||||||
|
memcpy(pIdentityContext->UserIdentifier, spuser, sizeof(pIdentityContext->UserIdentifier));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
log_debugf(
|
||||||
|
"User authentication failed user=%.*s effuser=%.*s applname=%.*s cspuser=%s cc=%d reason=%d",
|
||||||
|
trimmed_len(pIdentityContext->UserIdentifier, MQ_USER_ID_LENGTH),
|
||||||
|
pIdentityContext->UserIdentifier,
|
||||||
|
trimmed_len(pApplicationContext->EffectiveUserID, MQ_USER_ID_LENGTH),
|
||||||
|
pApplicationContext->EffectiveUserID,
|
||||||
|
trimmed_len(pApplicationContext->ApplName, MQ_APPL_NAME_LENGTH),
|
||||||
|
pApplicationContext->ApplName,
|
||||||
|
spuser,
|
||||||
|
*pCompCode,
|
||||||
|
*pReason);
|
||||||
|
}
|
||||||
|
if (spuser)
|
||||||
|
{
|
||||||
|
free(spuser);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called during MQDISC, as the inverse of the call to authenticate.
|
||||||
|
*/
|
||||||
|
static void MQENTRY mqhtpass_free_user(
|
||||||
|
PMQCHAR pQMgrName,
|
||||||
|
PMQZFP pFreeParms,
|
||||||
|
PMQBYTE pComponentData,
|
||||||
|
PMQLONG pContinuation,
|
||||||
|
|
||||||
|
PMQLONG pCompCode,
|
||||||
|
PMQLONG pReason)
|
||||||
|
{
|
||||||
|
log_debugf("mqhtpass_freeuser()");
|
||||||
|
*pCompCode = MQCC_WARNING;
|
||||||
|
*pReason = MQRC_NONE;
|
||||||
|
*pContinuation = MQZCI_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the authorization service is terminated.
|
||||||
|
*/
|
||||||
|
static void MQENTRY mqhtpass_terminate(
|
||||||
|
MQHCONFIG hc,
|
||||||
|
MQLONG Options,
|
||||||
|
PMQCHAR pQMgrName,
|
||||||
|
PMQBYTE pComponentData,
|
||||||
|
PMQLONG pCompCode,
|
||||||
|
PMQLONG pReason)
|
||||||
|
{
|
||||||
|
if (Options == MQZTO_PRIMARY)
|
||||||
|
{
|
||||||
|
log_infof("Terminating %s", NAME);
|
||||||
|
log_close();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
log_debugf("Terminating secondary");
|
||||||
|
}
|
||||||
|
*pCompCode = MQCC_OK;
|
||||||
|
*pReason = MQRC_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
© Copyright IBM Corporation 2017, 2020
|
© Copyright IBM Corporation 2017, 2022
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -18,22 +18,24 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"os/signal"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/ibm-messaging/mq-container/pkg/name"
|
"github.com/ibm-messaging/mq-container/pkg/name"
|
||||||
)
|
)
|
||||||
|
|
||||||
func queueManagerHealthy() (bool, error) {
|
func queueManagerHealthy(ctx context.Context) (bool, error) {
|
||||||
name, err := name.GetQueueManagerName()
|
name, err := name.GetQueueManagerName()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
// Specify the queue manager name, just in case someone's created a second queue manager
|
// Specify the queue manager name, just in case someone's created a second queue manager
|
||||||
// #nosec G204
|
// #nosec G204
|
||||||
cmd := exec.Command("dspmq", "-n", "-m", name)
|
cmd := exec.CommandContext(ctx, "dspmq", "-n", "-m", name)
|
||||||
// Run the command and wait for completion
|
// Run the command and wait for completion
|
||||||
out, err := cmd.CombinedOutput()
|
out, err := cmd.CombinedOutput()
|
||||||
fmt.Printf("%s", out)
|
fmt.Printf("%s", out)
|
||||||
@@ -41,19 +43,26 @@ func queueManagerHealthy() (bool, error) {
|
|||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
if !strings.Contains(string(out), "(RUNNING)") && !strings.Contains(string(out), "(RUNNING AS STANDBY)") && !strings.Contains(string(out), "(STARTING)") {
|
if !strings.Contains(string(out), "(RUNNING)") && !strings.Contains(string(out), "(RUNNING AS STANDBY)") && !strings.Contains(string(out), "(STARTING)") && !strings.Contains(string(out), "(REPLICA)") {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func doMain() int {
|
||||||
healthy, err := queueManagerHealthy()
|
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, os.Kill)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
healthy, err := queueManagerHealthy(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
os.Exit(2)
|
return 2
|
||||||
}
|
}
|
||||||
if !healthy {
|
if !healthy {
|
||||||
os.Exit(1)
|
return 1
|
||||||
}
|
}
|
||||||
os.Exit(0)
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
os.Exit(doMain())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
© Copyright IBM Corporation 2017, 2019
|
© Copyright IBM Corporation 2017, 2023
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -18,39 +18,59 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
|
||||||
"github.com/ibm-messaging/mq-container/internal/ready"
|
"github.com/ibm-messaging/mq-container/internal/ready"
|
||||||
"github.com/ibm-messaging/mq-container/pkg/name"
|
"github.com/ibm-messaging/mq-container/pkg/name"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func doMain() int {
|
||||||
|
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, os.Kill)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
// Check if runmqserver has indicated that it's finished configuration
|
// Check if runmqserver has indicated that it's finished configuration
|
||||||
r, err := ready.Check()
|
r, err := ready.Check()
|
||||||
if !r || err != nil {
|
if !r || err != nil {
|
||||||
os.Exit(1)
|
return 1
|
||||||
}
|
}
|
||||||
name, err := name.GetQueueManagerName()
|
name, err := name.GetQueueManagerName()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
os.Exit(1)
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the queue manager has a running listener
|
// Check if the queue manager has a running listener
|
||||||
if standby, _ := ready.IsRunningAsStandbyQM(name); !standby {
|
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")
|
conn, err := net.Dial("tcp", "127.0.0.1:1414")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
os.Exit(1)
|
return 1
|
||||||
}
|
}
|
||||||
err = conn.Close()
|
err = conn.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
}
|
}
|
||||||
} else {
|
return 0
|
||||||
|
case ready.StatusStandbyQM:
|
||||||
fmt.Printf("Detected queue manager running in standby mode")
|
fmt.Printf("Detected queue manager running in standby mode")
|
||||||
os.Exit(10)
|
return 10
|
||||||
|
case ready.StatusReplicaQM:
|
||||||
|
fmt.Printf("Detected queue manager running in replica mode")
|
||||||
|
return 20
|
||||||
|
default:
|
||||||
|
return 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
os.Exit(doMain())
|
||||||
|
}
|
||||||
|
|||||||
67
cmd/chkmqstarted/main.go
Normal file
67
cmd/chkmqstarted/main.go
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
© Copyright IBM Corporation 2021, 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// chkmqstarted checks that MQ has successfully started, by checking the output of the "dspmq" command
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"os/signal"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/ibm-messaging/mq-container/pkg/name"
|
||||||
|
)
|
||||||
|
|
||||||
|
func queueManagerStarted(ctx context.Context) (bool, error) {
|
||||||
|
name, err := name.GetQueueManagerName()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
// Specify the queue manager name, just in case someone's created a second queue manager
|
||||||
|
// #nosec G204
|
||||||
|
cmd := exec.CommandContext(ctx, "dspmq", "-n", "-m", name)
|
||||||
|
// Run the command and wait for completion
|
||||||
|
out, err := cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if !strings.Contains(string(out), "(RUNNING)") && !strings.Contains(string(out), "(RUNNING AS STANDBY)") && !strings.Contains(string(out), "(STARTING)") && !strings.Contains(string(out), "(REPLICA)") {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func doMain() int {
|
||||||
|
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, os.Kill)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
started, err := queueManagerStarted(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
if !started {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
os.Exit(doMain())
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
© Copyright IBM Corporation 2018, 2020
|
© Copyright IBM Corporation 2018, 2023
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -19,7 +19,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/ibm-messaging/mq-container/internal/htpasswd"
|
"github.com/ibm-messaging/mq-container/internal/htpasswd"
|
||||||
@@ -30,29 +30,21 @@ import (
|
|||||||
|
|
||||||
var log *logger.Logger
|
var log *logger.Logger
|
||||||
|
|
||||||
func setPassword(user string, password string) error {
|
|
||||||
// #nosec G204
|
|
||||||
cmd := exec.Command("sudo", "chpasswd")
|
|
||||||
stdin, err := cmd.StdinPipe()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fmt.Fprintf(stdin, "%s:%s", user, password)
|
|
||||||
err = stdin.Close()
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("Error closing password stdin: %v", err)
|
|
||||||
}
|
|
||||||
out, err := cmd.CombinedOutput()
|
|
||||||
if err != nil {
|
|
||||||
// Include the command output in the error
|
|
||||||
return fmt.Errorf("%v: %v", err.Error(), out)
|
|
||||||
}
|
|
||||||
log.Printf("Set password for \"%v\" user", user)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getLogFormat() string {
|
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 {
|
func getDebug() bool {
|
||||||
@@ -99,6 +91,7 @@ func logTermination(args ...interface{}) {
|
|||||||
// Write the message to the termination log. This is not the default place
|
// Write the message to the termination log. This is not the default place
|
||||||
// that Kubernetes will look for termination information.
|
// that Kubernetes will look for termination information.
|
||||||
log.Debugf("Writing termination message: %v", msg)
|
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)
|
err := ioutil.WriteFile("/run/termination-log", []byte(msg), 0660)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug(err)
|
log.Debug(err)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
© Copyright IBM Corporation 2017, 2020
|
© Copyright IBM Corporation 2017, 2023
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with 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
|
// Write the message to the termination log. This is not the default place
|
||||||
// that Kubernetes will look for termination information.
|
// that Kubernetes will look for termination information.
|
||||||
log.Debugf("Writing termination message: %v", msg)
|
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)
|
err := ioutil.WriteFile("/run/termination-log", []byte(msg), 0660)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug(err)
|
log.Debug(err)
|
||||||
@@ -58,7 +59,20 @@ func logTermination(args ...interface{}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getLogFormat() string {
|
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
|
// formatBasic formats a log message parsed from JSON, as "basic" text
|
||||||
@@ -79,6 +93,96 @@ func formatBasic(obj map[string]interface{}) string {
|
|||||||
if len(inserts) > 0 {
|
if len(inserts) > 0 {
|
||||||
return fmt.Sprintf("%s %s [%v]\n", obj["ibm_datetime"], obj["message"], strings.Join(inserts, ", "))
|
return fmt.Sprintf("%s %s [%v]\n", obj["ibm_datetime"], obj["message"], strings.Join(inserts, ", "))
|
||||||
}
|
}
|
||||||
|
// 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"])
|
return fmt.Sprintf("%s %s\n", obj["ibm_datetime"], obj["message"])
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,6 +204,16 @@ func mirrorQueueManagerErrorLogs(ctx context.Context, wg *sync.WaitGroup, name s
|
|||||||
return mirrorLog(ctx, wg, f, fromStart, mf, true)
|
return mirrorLog(ctx, wg, f, fromStart, mf, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// mirrorHTPasswdLogs starts a goroutine to mirror the contents of the MQ HTPasswd authorization service's log
|
||||||
|
func mirrorHTPasswdLogs(ctx context.Context, wg *sync.WaitGroup, name string, fromStart bool, mf mirrorFunc) (chan error, error) {
|
||||||
|
return mirrorLog(ctx, wg, "/var/mqm/errors/mqhtpass.json", false, mf, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// mirrorWebServerLogs starts a goroutine to mirror the contents of the Liberty web server messages.log
|
||||||
|
func mirrorWebServerLogs(ctx context.Context, wg *sync.WaitGroup, name string, fromStart bool, mf mirrorFunc) (chan error, error) {
|
||||||
|
return mirrorLog(ctx, wg, "/var/mqm/web/installations/Installation1/servers/mqweb/logs/messages.log", fromStart, mf, true)
|
||||||
|
}
|
||||||
|
|
||||||
func getDebug() bool {
|
func getDebug() bool {
|
||||||
debug := os.Getenv("DEBUG")
|
debug := os.Getenv("DEBUG")
|
||||||
if debug == "true" || debug == "1" {
|
if debug == "true" || debug == "1" {
|
||||||
@@ -119,15 +233,27 @@ func configureLogger(name string) (mirrorFunc, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return func(msg string, isQMLog bool) bool {
|
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)
|
obj, err := processLogMessage(msg)
|
||||||
if err == nil && isQMLog && filterQMLogMessage(obj) {
|
if err == nil && isQMLog && filterQMLogMessage(obj) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed to unmarshall JSON - %v", msg)
|
log.Printf("Failed to unmarshall JSON in log message - %v", msg)
|
||||||
} else {
|
} else {
|
||||||
fmt.Println(msg)
|
fmt.Println(msg)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// The log being mirrored isn't JSON, so wrap it in a simple JSON message
|
||||||
|
// MQ error logs are usually JSON, but this is useful for Liberty logs - usually expect WLP_LOGGING_MESSAGE_FORMAT=JSON to be set when mirroring Liberty logs.
|
||||||
|
fmt.Printf("{\"message\":\"%s\"}\n", msg)
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}, nil
|
}, nil
|
||||||
case "basic":
|
case "basic":
|
||||||
@@ -136,16 +262,27 @@ func configureLogger(name string) (mirrorFunc, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return func(msg string, isQMLog bool) bool {
|
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
|
// Parse the JSON message, and print a simplified version
|
||||||
obj, err := processLogMessage(msg)
|
obj, err := processLogMessage(msg)
|
||||||
if err == nil && isQMLog && filterQMLogMessage(obj) {
|
if err == nil && isQMLog && filterQMLogMessage(obj) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed to unmarshall JSON - %v", err)
|
log.Printf("Failed to unmarshall JSON in log message - %v", err)
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf(formatBasic(obj))
|
fmt.Print(formatBasic(obj))
|
||||||
// fmt.Printf(formatSimple(obj["ibm_datetime"].(string), obj["message"].(string)))
|
}
|
||||||
|
} else {
|
||||||
|
// The log being mirrored isn't JSON, so just print it.
|
||||||
|
// MQ error logs are usually JSON, but this is useful for Liberty logs - usually expect WLP_LOGGING_MESSAGE_FORMAT=JSON to be set when mirroring Liberty logs.
|
||||||
|
fmt.Println(msg)
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}, nil
|
}, nil
|
||||||
@@ -172,6 +309,16 @@ func filterQMLogMessage(obj map[string]interface{}) bool {
|
|||||||
return false
|
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() {
|
func logDiagnostics() {
|
||||||
if getDebug() {
|
if getDebug() {
|
||||||
log.Debug("--- Start Diagnostics ---")
|
log.Debug("--- Start Diagnostics ---")
|
||||||
@@ -213,3 +360,73 @@ func logDiagnostics() {
|
|||||||
log.Debug("--- End Diagnostics ---")
|
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");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -18,6 +18,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"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, 2020
|
© Copyright IBM Corporation 2017, 2023
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -24,6 +24,8 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"sync"
|
"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/metrics"
|
||||||
"github.com/ibm-messaging/mq-container/internal/ready"
|
"github.com/ibm-messaging/mq-container/internal/ready"
|
||||||
"github.com/ibm-messaging/mq-container/internal/tls"
|
"github.com/ibm-messaging/mq-container/internal/tls"
|
||||||
@@ -143,28 +145,24 @@ func doMain() error {
|
|||||||
// Print out versioning information
|
// Print out versioning information
|
||||||
logVersionInfo()
|
logVersionInfo()
|
||||||
|
|
||||||
keyLabel, cmsKeystore, p12Truststore, err := tls.ConfigureTLSKeystores()
|
// Determine FIPS compliance level
|
||||||
|
fips.ProcessFIPSType(log)
|
||||||
|
|
||||||
|
keyLabel, defaultCmsKeystore, defaultP12Truststore, err := tls.ConfigureDefaultTLSKeystores()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logTermination(err)
|
logTermination(err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = tls.ConfigureTLS(keyLabel, cmsKeystore, *devFlag, log)
|
err = tls.ConfigureTLS(keyLabel, defaultCmsKeystore, *devFlag, log)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logTermination(err)
|
logTermination(err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = postInit(name, keyLabel, p12Truststore)
|
//Validate MQ_LOG_CONSOLE_SOURCE variable
|
||||||
if err != nil {
|
if !isLogConsoleSourceValid() {
|
||||||
logTermination(err)
|
log.Println("One or more invalid value is provided for MQ_LOGGING_CONSOLE_SOURCE. Allowed values are 'qmgr' & 'web' in csv format")
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
newQM, err := createQueueManager(name, *devFlag)
|
|
||||||
if err != nil {
|
|
||||||
logTermination(err)
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
@@ -177,17 +175,82 @@ func doMain() error {
|
|||||||
log.Debug("Cancel log mirroring")
|
log.Debug("Cancel log mirroring")
|
||||||
cancelMirror()
|
cancelMirror()
|
||||||
}()
|
}()
|
||||||
// TODO: Use the error channel
|
|
||||||
|
//For mirroring web server logs if source variable is set
|
||||||
|
if checkLogSourceForMirroring("web") {
|
||||||
|
// Always log from the end of the web server messages.log, because the log rotation should happen as soon as the web server starts
|
||||||
|
_, err = mirrorWebServerLogs(ctx, &wg, name, false, mf)
|
||||||
|
if err != nil {
|
||||||
|
logTermination(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = postInit(name, keyLabel, defaultP12Truststore)
|
||||||
|
if err != nil {
|
||||||
|
logTermination(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if os.Getenv("MQ_NATIVE_HA") == "true" {
|
||||||
|
err = ha.ConfigureNativeHA(log)
|
||||||
|
if err != nil {
|
||||||
|
logTermination(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Post FIPS initialization processing
|
||||||
|
fips.PostInit(log)
|
||||||
|
|
||||||
|
enableTraceCrtmqm := os.Getenv("MQ_ENABLE_TRACE_CRTMQM")
|
||||||
|
if enableTraceCrtmqm == "true" || enableTraceCrtmqm == "1" {
|
||||||
|
err = startMQTrace()
|
||||||
|
if err != nil {
|
||||||
|
logTermination(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
newQM, err := createQueueManager(name, *devFlag)
|
||||||
|
if err != nil {
|
||||||
|
logTermination(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if enableTraceCrtmqm == "true" || enableTraceCrtmqm == "1" {
|
||||||
|
err = endMQTrace()
|
||||||
|
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)
|
_, err = mirrorSystemErrorLogs(ctx, &wg, mf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logTermination(err)
|
logTermination(err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Mirror queue manager logs
|
||||||
_, err = mirrorQueueManagerErrorLogs(ctx, &wg, name, newQM, mf)
|
_, err = mirrorQueueManagerErrorLogs(ctx, &wg, name, newQM, mf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logTermination(err)
|
logTermination(err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if *devFlag {
|
||||||
|
_, err = mirrorHTPasswdLogs(ctx, &wg, name, newQM, mf)
|
||||||
|
if err != nil {
|
||||||
|
logTermination(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
err = updateCommandLevel()
|
err = updateCommandLevel()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logTermination(err)
|
logTermination(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.
|
// the file is open before the queue manager is created or started.
|
||||||
// Otherwise, there would be the potential for a nearly-full file to
|
// Otherwise, there would be the potential for a nearly-full file to
|
||||||
// rotate before the goroutine had a chance to open it.
|
// 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)
|
f, err = os.OpenFile(path, os.O_RDONLY, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -122,6 +123,7 @@ func mirrorLog(ctx context.Context, wg *sync.WaitGroup, path string, fromStart b
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
log.Debugf("File exists: %v, %v", path, fi.Size())
|
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)
|
f, err = os.OpenFile(path, os.O_RDONLY, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
@@ -169,6 +171,7 @@ func mirrorLog(ctx context.Context, wg *sync.WaitGroup, path string, fromStart b
|
|||||||
}
|
}
|
||||||
// Re-open file
|
// Re-open file
|
||||||
log.Debugf("Re-opening error log file %v", path)
|
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)
|
f, err = os.OpenFile(path, os.O_RDONLY, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err)
|
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");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -18,6 +18,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"github.com/ibm-messaging/mq-container/internal/fips"
|
||||||
"github.com/ibm-messaging/mq-container/internal/tls"
|
"github.com/ibm-messaging/mq-container/internal/tls"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -25,6 +26,15 @@ import (
|
|||||||
func postInit(name, keyLabel string, p12Truststore tls.KeyStoreData) error {
|
func postInit(name, keyLabel string, p12Truststore tls.KeyStoreData) error {
|
||||||
enableWebServer := os.Getenv("MQ_ENABLE_EMBEDDED_WEB_SERVER")
|
enableWebServer := os.Getenv("MQ_ENABLE_EMBEDDED_WEB_SERVER")
|
||||||
if enableWebServer == "true" || enableWebServer == "1" {
|
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)
|
// Configure the web server (if enabled)
|
||||||
webKeystore, err := configureWebServer(keyLabel, p12Truststore)
|
webKeystore, err := configureWebServer(keyLabel, p12Truststore)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -35,6 +45,7 @@ func postInit(name, keyLabel string, p12Truststore tls.KeyStoreData) error {
|
|||||||
if len(p12Truststore.TrustedCerts) == 0 {
|
if len(p12Truststore.TrustedCerts) == 0 {
|
||||||
webTruststoreRef = "MQWebKeyStore"
|
webTruststoreRef = "MQWebKeyStore"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start the web server, in the background (if installed)
|
// Start the web server, in the background (if installed)
|
||||||
// WARNING: No error handling or health checking available for the web server
|
// WARNING: No error handling or health checking available for the web server
|
||||||
go func() {
|
go func() {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
© Copyright IBM Corporation 2017, 2020
|
© Copyright IBM Corporation 2017, 2023
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -16,6 +16,7 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
@@ -27,6 +28,7 @@ import (
|
|||||||
"github.com/ibm-messaging/mq-container/internal/command"
|
"github.com/ibm-messaging/mq-container/internal/command"
|
||||||
containerruntime "github.com/ibm-messaging/mq-container/internal/containerruntime"
|
containerruntime "github.com/ibm-messaging/mq-container/internal/containerruntime"
|
||||||
"github.com/ibm-messaging/mq-container/internal/mqscredact"
|
"github.com/ibm-messaging/mq-container/internal/mqscredact"
|
||||||
|
"github.com/ibm-messaging/mq-container/internal/mqversion"
|
||||||
"github.com/ibm-messaging/mq-container/internal/ready"
|
"github.com/ibm-messaging/mq-container/internal/ready"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -53,23 +55,36 @@ func createDirStructure() error {
|
|||||||
func createQueueManager(name string, devMode bool) (bool, error) {
|
func createQueueManager(name string, devMode bool) (bool, error) {
|
||||||
log.Printf("Creating queue manager %v", name)
|
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()
|
mounts, err := containerruntime.GetMounts()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error getting mounts for queue manager")
|
log.Printf("Error getting mounts for queue manager")
|
||||||
return false, err
|
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
|
// Check if 'qm.ini' configuration file exists for the queue manager
|
||||||
// TODO : handle possible race condition - use a file lock?
|
// TODO : handle possible race condition - use a file lock?
|
||||||
dataDir := getQueueManagerDataDir(mounts, name)
|
|
||||||
_, err = os.Stat(filepath.Join(dataDir, "qm.ini"))
|
_, err = os.Stat(filepath.Join(dataDir, "qm.ini"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// If 'qm.ini' is not found - run 'crtmqm' to create a new queue manager
|
// If 'qm.ini' is not found - run 'crtmqm' to create a new queue manager
|
||||||
@@ -94,6 +109,25 @@ func createQueueManager(name string, devMode bool) (bool, error) {
|
|||||||
return true, nil
|
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 {
|
func updateCommandLevel() error {
|
||||||
level, ok := os.LookupEnv("MQ_CMDLEVEL")
|
level, ok := os.LookupEnv("MQ_CMDLEVEL")
|
||||||
if ok && level != "" {
|
if ok && level != "" {
|
||||||
@@ -112,9 +146,13 @@ func startQueueManager(name string) error {
|
|||||||
out, rc, err := command.Run("strmqm", "-x", name)
|
out, rc, err := command.Run("strmqm", "-x", name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// 30=standby queue manager started, which is fine
|
// 30=standby queue manager started, which is fine
|
||||||
|
// 94=native HA replica started, which is fine
|
||||||
if rc == 30 {
|
if rc == 30 {
|
||||||
log.Printf("Started standby queue manager")
|
log.Printf("Started standby queue manager")
|
||||||
return nil
|
return nil
|
||||||
|
} else if rc == 94 {
|
||||||
|
log.Printf("Started replica queue manager")
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
log.Printf("Error %v starting queue manager: %v", rc, string(out))
|
log.Printf("Error %v starting queue manager: %v", rc, string(out))
|
||||||
return err
|
return err
|
||||||
@@ -126,17 +164,18 @@ func startQueueManager(name string) error {
|
|||||||
func stopQueueManager(name string) error {
|
func stopQueueManager(name string) error {
|
||||||
log.Println("Stopping queue manager")
|
log.Println("Stopping queue manager")
|
||||||
qmGracePeriod := os.Getenv("MQ_GRACE_PERIOD")
|
qmGracePeriod := os.Getenv("MQ_GRACE_PERIOD")
|
||||||
isStandby, err := ready.IsRunningAsStandbyQM(name)
|
status, err := ready.Status(context.Background(), name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error getting status for queue manager %v: %v", name, err.Error())
|
log.Printf("Error getting status for queue manager %v: %v", name, err.Error())
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
isStandby := status.StandbyQM()
|
||||||
args := []string{"-w", "-r", "-tp", qmGracePeriod, name}
|
args := []string{"-w", "-r", "-tp", qmGracePeriod, name}
|
||||||
if os.Getenv("MQ_MULTI_INSTANCE") == "true" {
|
if os.Getenv("MQ_MULTI_INSTANCE") == "true" {
|
||||||
if isStandby {
|
if isStandby {
|
||||||
args = []string{"-x", name}
|
args = []string{"-x", name}
|
||||||
} else {
|
} else {
|
||||||
args = []string{"-s", "-w", "-r", "-tp", qmGracePeriod, name}
|
args = []string{"-s", "-w", "-tp", qmGracePeriod, name}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
out, rc, err := command.Run("endmqm", args...)
|
out, rc, err := command.Run("endmqm", args...)
|
||||||
@@ -203,9 +242,28 @@ func getQueueManagerDataDir(mounts map[string]string, name string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getCreateQueueManagerArgs(mounts map[string]string, name string, devMode bool) []string {
|
func getCreateQueueManagerArgs(mounts map[string]string, name string, devMode bool) []string {
|
||||||
|
|
||||||
|
mqversionBase := "9.2.1.0"
|
||||||
|
|
||||||
|
// use "UserExternal" only if we are 9.2.1.0 or above.
|
||||||
|
oaVal := "user"
|
||||||
|
mqVersionCheck, err := mqversion.Compare(mqversionBase)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error comparing MQ versions for oa,rc: %v", mqVersionCheck)
|
||||||
|
}
|
||||||
|
if mqVersionCheck >= 0 {
|
||||||
|
oaVal = "UserExternal"
|
||||||
|
}
|
||||||
|
|
||||||
|
//build args
|
||||||
args := []string{"-ii", "/etc/mqm/", "-ic", "/etc/mqm/", "-q", "-p", "1414"}
|
args := []string{"-ii", "/etc/mqm/", "-ic", "/etc/mqm/", "-q", "-p", "1414"}
|
||||||
|
|
||||||
|
if os.Getenv("MQ_NATIVE_HA") == "true" {
|
||||||
|
args = append(args, "-lr", os.Getenv("HOSTNAME"))
|
||||||
|
}
|
||||||
if devMode {
|
if devMode {
|
||||||
args = append(args, "-oa", "user")
|
args = append(args, "-oa", oaVal)
|
||||||
}
|
}
|
||||||
if _, ok := mounts["/mnt/mqm-log"]; ok {
|
if _, ok := mounts["/mnt/mqm-log"]; ok {
|
||||||
args = append(args, "-ld", "/mnt/mqm-log/log")
|
args = append(args, "-ld", "/mnt/mqm-log/log")
|
||||||
@@ -213,6 +271,14 @@ func getCreateQueueManagerArgs(mounts map[string]string, name string, devMode bo
|
|||||||
if _, ok := mounts["/mnt/mqm-data"]; ok {
|
if _, ok := mounts["/mnt/mqm-data"]; ok {
|
||||||
args = append(args, "-md", "/mnt/mqm-data/qmgrs")
|
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)
|
args = append(args, name)
|
||||||
return args
|
return args
|
||||||
}
|
}
|
||||||
@@ -262,7 +328,8 @@ func updateQMini(qmname string) error {
|
|||||||
if strings.Contains(qminiConfigStr, "ServiceComponent:") {
|
if strings.Contains(qminiConfigStr, "ServiceComponent:") {
|
||||||
var re = regexp.MustCompile(`(?m)^.*ServiceComponent.*$\s^.*Service.*$\s^.*Name.*$\s^.*Module.*$\s^.*ComponentDataSize.*$`)
|
var re = regexp.MustCompile(`(?m)^.*ServiceComponent.*$\s^.*Service.*$\s^.*Name.*$\s^.*Module.*$\s^.*ComponentDataSize.*$`)
|
||||||
curFile := re.ReplaceAllString(qminiConfigStr, "")
|
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)
|
err := ioutil.WriteFile(qmgrDir, []byte(curFile), 0660)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
© Copyright IBM Corporation 2017, 2018
|
© Copyright IBM Corporation 2017, 2022
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -33,8 +33,8 @@ func signalHandler(qmgr string) chan int {
|
|||||||
control := make(chan int)
|
control := make(chan int)
|
||||||
// Use separate channels for the signals, to avoid SIGCHLD signals swamping
|
// Use separate channels for the signals, to avoid SIGCHLD signals swamping
|
||||||
// the buffer, and preventing other signals.
|
// the buffer, and preventing other signals.
|
||||||
stopSignals := make(chan os.Signal)
|
stopSignals := make(chan os.Signal, 1)
|
||||||
reapSignals := make(chan os.Signal)
|
reapSignals := make(chan os.Signal, 1)
|
||||||
signal.Notify(stopSignals, syscall.SIGTERM, syscall.SIGINT)
|
signal.Notify(stopSignals, syscall.SIGTERM, syscall.SIGINT)
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
|
|||||||
@@ -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");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -62,8 +62,11 @@ func startWebServer(webKeystore, webkeystorePW, webTruststoreRef string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func configureSSO(p12TrustStore tls.KeyStoreData, webKeystore string) (string, error) {
|
func configureSSO(p12TrustStore tls.KeyStoreData, webKeystore string) (string, error) {
|
||||||
|
requiredEnvVars := []string{}
|
||||||
|
_, set := os.LookupEnv("MQ_ZEN_INTERNAL_ENDPOINT")
|
||||||
|
if !set {
|
||||||
// Ensure all required environment variables are set for SSO
|
// Ensure all required environment variables are set for SSO
|
||||||
requiredEnvVars := []string{
|
requiredEnvVars = []string{
|
||||||
"MQ_OIDC_CLIENT_ID",
|
"MQ_OIDC_CLIENT_ID",
|
||||||
"MQ_OIDC_CLIENT_SECRET",
|
"MQ_OIDC_CLIENT_SECRET",
|
||||||
"MQ_OIDC_UNIQUE_USER_IDENTIFIER",
|
"MQ_OIDC_UNIQUE_USER_IDENTIFIER",
|
||||||
@@ -72,6 +75,19 @@ func configureSSO(p12TrustStore tls.KeyStoreData, webKeystore string) (string, e
|
|||||||
"MQ_OIDC_JWK_ENDPOINT",
|
"MQ_OIDC_JWK_ENDPOINT",
|
||||||
"MQ_OIDC_ISSUER_IDENTIFIER",
|
"MQ_OIDC_ISSUER_IDENTIFIER",
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// Ensure all required environment variables are set for Zen SSO
|
||||||
|
requiredEnvVars = []string{
|
||||||
|
"MQ_ZEN_UNIQUE_USER_IDENTIFIER",
|
||||||
|
"MQ_ZEN_INTERNAL_ENDPOINT",
|
||||||
|
"MQ_ZEN_ISSUER_IDENTIFIER",
|
||||||
|
"MQ_ZEN_AUDIENCES",
|
||||||
|
"MQ_ZEN_CONTEXT_NAME",
|
||||||
|
"MQ_ZEN_BASE_URI",
|
||||||
|
"MQ_ZEN_CONTEXT_NAMESPACE",
|
||||||
|
"IAM_URL",
|
||||||
|
}
|
||||||
|
}
|
||||||
for _, envVar := range requiredEnvVars {
|
for _, envVar := range requiredEnvVars {
|
||||||
if len(os.Getenv(envVar)) == 0 {
|
if len(os.Getenv(envVar)) == 0 {
|
||||||
return "", fmt.Errorf("%v must be set when MQ_BETA_ENABLE_SSO=true", envVar)
|
return "", fmt.Errorf("%v must be set when MQ_BETA_ENABLE_SSO=true", envVar)
|
||||||
@@ -181,3 +197,25 @@ func configureWebServer(keyLabel string, p12Truststore tls.KeyStoreData) (string
|
|||||||
|
|
||||||
return webKeystore, err
|
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 is the fully qualified MQ version number to build
|
||||||
MQ_VERSION ?= 9.2.0.0
|
MQ_VERSION ?= 9.3.3.0
|
||||||
|
|
||||||
###########################################################################################################################################################
|
###########################################################################################################################################################
|
||||||
|
|||||||
@@ -12,6 +12,6 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
FROM fedora:32
|
FROM fedora:41
|
||||||
RUN yum install skopeo -y -qq
|
RUN yum install skopeo -y -qq
|
||||||
ENTRYPOINT [ "skopeo" ]
|
ENTRYPOINT [ "skopeo" ]
|
||||||
|
|||||||
38
docs/building.md
Normal file → Executable file
38
docs/building.md
Normal file → Executable file
@@ -4,58 +4,44 @@
|
|||||||
|
|
||||||
You need to have the following tools installed:
|
You need to have the following tools installed:
|
||||||
|
|
||||||
* [Docker](https://www.docker.com/) V17.06.1 or later, or [Podman](https://podman.io) V1.0 or later
|
* [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/)
|
* [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.
|
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.
|
||||||
|
|
||||||
You will also need a [Red Hat Account](https://access.redhat.com) to be able to access the Red Hat Registry.
|
|
||||||
|
|
||||||
## Building a production image
|
## Building a production image
|
||||||
|
|
||||||
From MQ 9.2.X, the MQ container adds support for MQ Long Term Support (LTS) **production licensed** releases.
|
From MQ 9.2.X, the MQ container adds support for MQ Long Term Support (LTS) **production licensed** releases.
|
||||||
|
|
||||||
### MQ Continuous Delivery (CD)
|
### Building MQ 9.3 Long Term Support (LTS) and Continuous Delivery (CD)
|
||||||
|
|
||||||
This procedure works for building the MQ Continuous Delivery release, on `amd64`, `ppc64le` and `s390x` architectures.
|
**Note**: MQ 9.3 is the latest MQ version with MQ Long Term Support (LTS), as well as being the latest Continuous Delivery (CD) version.
|
||||||
|
|
||||||
|
The procedure below is for building the 9.3 release, on `amd64`, `ppc64le` and `s390x` architectures.
|
||||||
|
|
||||||
1. Create a `downloads` directory in the root of this repository
|
1. Create a `downloads` directory in the root of this repository
|
||||||
2. Download MQ from [IBM Passport Advantage](https://www.ibm.com/software/passportadvantage/) or [IBM Fix Central](https://www.ibm.com/support/fixcentral), and place the downloaded file (for example, `IBM_MQ_9.2.0_LINUX_X86-64_NOINST.tar.gz`) in the `downloads` directory
|
2. Download MQ from [IBM Passport Advantage](https://www.ibm.com/software/passportadvantage/). Identify the correct 'Long Term Support Release for Containers' eImage part number for your architecture from the 9.3.0 LTS tab at https://www.ibm.com/support/pages/downloading-ibm-mq-930
|
||||||
3. Login to the Red Hat Registry: `docker login registry.redhat.io` using your Customer Portal credentials.
|
3. Ensure the `tar.gz` file is in the `downloads` directory
|
||||||
4. Run `make build-advancedserver`
|
4. Run `make build-advancedserver`
|
||||||
|
|
||||||
> **Warning**: Note that from MQ 9.2.X CD, the MQ container build uses a 'No-Install' MQ Package, available under `IBM MQ V9.2.x Continuous Delivery Release components eAssembly, part no. CJ7CNML`
|
|
||||||
|
|
||||||
If you have an MQ archive file with a different file name, you can specify a particular file (which must be in the `downloads` directory). You should also specify the MQ version, so that the resulting image is tagged correctly, for example:
|
If you have an MQ archive file with a different file name, you can specify a particular file (which must be in the `downloads` directory). You should also specify the MQ version, so that the resulting image is tagged correctly, for example:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
MQ_ARCHIVE=mq-1.2.3.4.tar.gz MQ_VERSION=1.2.3.4 make build-advancedserver
|
MQ_ARCHIVE=mq-1.2.3.4.tar.gz MQ_VERSION=1.2.3.4 make build-advancedserver
|
||||||
```
|
```
|
||||||
|
|
||||||
### MQ Long Term Support (LTS)
|
### Building previous MQ Long Term Support (LTS)
|
||||||
|
|
||||||
This procedure works for building the MQ Long Term Support release, on `amd64`, `ppc64le` and `s390x` architectures.
|
**Note**: MQ 9.3 is the latest MQ version with MQ Long Term Support (LTS), as well as being the latest Continuous Delivery (CD) version. Therefore, to build build 9.3.0.X, follow the [instructions above for MQ 9.3](#building-mq-93-long-term-support-lts-and-continuous-delivery-cd).
|
||||||
|
|
||||||
1. Create a `downloads` directory in the root of this repository
|
However, if you wish to build the previous MQ LTS, use the [instructions](https://github.ibm.com/mq-cloudpak/mq-container/blob/v9.2.0.x-eus/docs/building.md#mq-long-term-support-lts) in the `v9.2.0.x-eus` branch.
|
||||||
2. Download MQ from [IBM Passport Advantage](https://www.ibm.com/software/passportadvantage/) or [IBM Fix Central](https://www.ibm.com/support/fixcentral), and place the downloaded file (for example, `9.2.0.1-IBM-MQ-Advanced-Non-Install-LinuxX86.tar.gz`) in the `downloads` directory
|
|
||||||
3. Login to the Red Hat Registry: `docker login registry.redhat.io` using your Customer Portal credentials.
|
|
||||||
4. Run `LTS=true make build-advancedserver`
|
|
||||||
|
|
||||||
> **Warning**: Note that from MQ 9.2 LTS, the MQ container build uses a 'No-Install' MQ Package, available under `IBM MQ V9.2 Long Term Support Release components eAssembly, part no. CXXXXXX`
|
|
||||||
|
|
||||||
If you have an MQ archive file with a different file name, you can specify a particular file (which must be in the `downloads` directory). You should also specify the MQ version, so that the resulting image is tagged correctly, for example:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
MQ_ARCHIVE=mq-1.2.3.4.tar.gz MQ_VERSION=1.2.3.4 LTS=true make build-advancedserver
|
|
||||||
```
|
|
||||||
|
|
||||||
## Building a developer image
|
## Building a developer image
|
||||||
|
|
||||||
Login to the Red Hat Registry: `docker login registry.redhat.io` using your Customer Portal credentials.
|
Run `make build-devserver`, which will download the latest version of MQ Advanced for Developers. This is available on the `amd64` and `arm64` (Apple Silicon) architectures.
|
||||||
Run `make build-devserver`, which will download the latest version of MQ Advanced for Developers from IBM developerWorks. This is currently only available on the `amd64` architecture.
|
|
||||||
|
|
||||||
You can use the environment variable `MQ_ARCHIVE_DEV` to specify an alternative local file to install from (which must be in the `downloads` directory).
|
You can use the environment variable `MQ_ARCHIVE_DEV` to specify an alternative local file to install from (which must be in the `downloads` directory).
|
||||||
|
|
||||||
## Installed components
|
## 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`.
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ Two channels are created, one for administration, the other for normal messaging
|
|||||||
|
|
||||||
## Web Console
|
## Web Console
|
||||||
|
|
||||||
By default the MQ Advanced for Developers image will start the IBM MQ Web Console that allows you to administer your Queue Manager running on your container. When the web console has been started, you can access it by opening a web browser and navigating to https://<Container IP>:9443/ibmmq/console. Where <Container IP> is replaced by the IP address of your running container.
|
By default the MQ Advanced for Developers image will start the IBM MQ Web Console that allows you to administer your Queue Manager running on your container. When the web console has been started, you can access it by opening a web browser and navigating to `https://<Container IP>:9443/ibmmq/console`. Where `<Container IP>` is replaced by the IP address of your running container.
|
||||||
|
|
||||||
When you navigate to this page you may be presented with a security exception warning. This happens because, by default, the web console creates a self-signed certificate to use for the HTTPS operations. This certificate is not trusted by your browser and has an incorrect distinguished name.
|
When you navigate to this page you may be presented with a security exception warning. This happens because, by default, the web console creates a self-signed certificate to use for the HTTPS operations. This certificate is not trusted by your browser and has an incorrect distinguished name.
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ The resulting Docker image contains the following:
|
|||||||
- `runmqdevserver` - The main process for MQ Advanced for Developers
|
- `runmqdevserver` - The main process for MQ Advanced for Developers
|
||||||
- `chkmqhealthy` - Checks the health of the queue manager. This can be used by (say) a Kubernetes liveness probe.
|
- `chkmqhealthy` - Checks the health of the queue manager. This can be used by (say) a Kubernetes liveness probe.
|
||||||
- `chkmqready` - Checks if the queue manager is ready for work. This can be used by (say) a Kubernetes readiness probe.
|
- `chkmqready` - Checks if the queue manager is ready for work. This can be used by (say) a Kubernetes readiness probe.
|
||||||
|
- `chkmqstarted` - Checks if the queue manager has successfully started. This can be used by (say) a Kubernetes startup probe.
|
||||||
|
|
||||||
## runmqserver
|
## runmqserver
|
||||||
The `runmqserver` command has the following responsibilities:
|
The `runmqserver` command has the following responsibilities:
|
||||||
|
|||||||
@@ -24,6 +24,6 @@ Use an administrative tool or your application to connect to queue manager using
|
|||||||
|
|
||||||
#### Troubleshooting
|
#### Troubleshooting
|
||||||
|
|
||||||
A log file named `amqpasdev.log` is generated under `/var/mqm/errors` directory path of the container. This file will contain all the failed connection authentication requests.
|
A log file named `mqhtpass.log` is generated under `/var/mqm/errors` directory path of the container. This file will contain all the failed connection authentication requests. Additional information is logged to this file if the environment variable `DEBUG` is set to `true`.
|
||||||
|
|
||||||
**Please note**: This log file is based on circular logging and the maximum size is restricted to 1MB.
|
**Please note**: This log file will be wiped when the queue manager is next started.
|
||||||
|
|||||||
@@ -16,20 +16,5 @@ docker run \
|
|||||||
--env LICENSE=accept \
|
--env LICENSE=accept \
|
||||||
--env MQ_QMGR_NAME=QM1 \
|
--env MQ_QMGR_NAME=QM1 \
|
||||||
--detach \
|
--detach \
|
||||||
ibm-mqadvanced-server:9.2.0.0-amd64
|
ibm-mqadvanced-server:9.3.3.0-amd64
|
||||||
```
|
|
||||||
|
|
||||||
The MQ Advanced for Developers image does require the "chown", "setuid", "setgid" and "audit_write" capabilities (plus "dac_override" if you're using an image based on Red Hat Enterprise Linux). This is because it uses the "sudo" command to change passwords inside the container. For example, in Docker, you could do the following:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
docker run \
|
|
||||||
--cap-drop=ALL \
|
|
||||||
--cap-add=CHOWN \
|
|
||||||
--cap-add=SETUID \
|
|
||||||
--cap-add=SETGID \
|
|
||||||
--cap-add=AUDIT_WRITE \
|
|
||||||
--env LICENSE=accept \
|
|
||||||
--env MQ_QMGR_NAME=QM1 \
|
|
||||||
--detach \
|
|
||||||
ibm-mqadvanced-server-dev:9.2.0.0-amd64
|
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -2,10 +2,9 @@
|
|||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
You need to ensure you have the following tools installed:
|
You need to ensure you have the following tools installed:
|
||||||
* [Docker](https://www.docker.com/)
|
* [Docker](https://www.docker.com/) 19.03 or higher (API version 1.40)
|
||||||
* [GNU make](https://www.gnu.org/software/make/)
|
* [GNU make](https://www.gnu.org/software/make/)
|
||||||
* [Go](https://golang.org/) - only needed for running the tests
|
* [Go](https://golang.org/) - only needed for running the tests
|
||||||
* [dep](https://github.com/golang/dep) (official Go dependency management tool) - needed to prepare for running the tests
|
|
||||||
|
|
||||||
## Running the tests
|
## Running the tests
|
||||||
There are two main sets of tests:
|
There are two main sets of tests:
|
||||||
@@ -18,14 +17,14 @@ There are two main sets of tests:
|
|||||||
The Docker tests can be run locally on a machine with Docker. For example:
|
The Docker tests can be run locally on a machine with Docker. For example:
|
||||||
|
|
||||||
```
|
```
|
||||||
make devserver
|
make test-devserver
|
||||||
make advancedserver
|
make test-advancedserver
|
||||||
```
|
```
|
||||||
|
|
||||||
You can specify the image to use directly by using the `MQ_IMAGE_ADVANCEDSERVER` or `MQ_IMAGE_DEVSERVER` variables, for example:
|
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.2.0.0-amd64 make test-advancedserver
|
MQ_IMAGE_ADVANCEDSERVER=ibm-mqadvanced-server:9.3.3.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:
|
You can pass parameters to `go test` with an environment variable. For example, to run the "TestGoldenPath" test, run the following command:
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ docker run \
|
|||||||
--publish 1414:1414 \
|
--publish 1414:1414 \
|
||||||
--publish 9443:9443 \
|
--publish 9443:9443 \
|
||||||
--detach \
|
--detach \
|
||||||
ibmcom/mq
|
icr.io/ibm-messaging/mq
|
||||||
```
|
```
|
||||||
|
|
||||||
## Running with the default configuration and a volume
|
## Running with the default configuration and a volume
|
||||||
@@ -34,7 +34,7 @@ docker run \
|
|||||||
--publish 9443:9443 \
|
--publish 9443:9443 \
|
||||||
--detach \
|
--detach \
|
||||||
--volume qm1data:/mnt/mqm \
|
--volume qm1data:/mnt/mqm \
|
||||||
ibmcom/mq
|
icr.io/ibm-messaging/mq
|
||||||
```
|
```
|
||||||
|
|
||||||
The Docker image always uses `/mnt/mqm` for MQ data, which is correctly linked for you under `/var/mqm` at runtime. This is to handle problems with file permissions on some platforms.
|
The Docker image always uses `/mnt/mqm` for MQ data, which is correctly linked for you under `/var/mqm` at runtime. This is to handle problems with file permissions on some platforms.
|
||||||
@@ -51,7 +51,7 @@ docker run \
|
|||||||
--publish 9443:9443 \
|
--publish 9443:9443 \
|
||||||
--publish 9157:9157 \
|
--publish 9157:9157 \
|
||||||
--detach \
|
--detach \
|
||||||
ibmcom/mq
|
icr.io/ibm-messaging/mq
|
||||||
```
|
```
|
||||||
|
|
||||||
## Customizing the queue manager configuration
|
## Customizing the queue manager configuration
|
||||||
@@ -60,14 +60,14 @@ You can customize the configuration in several ways:
|
|||||||
|
|
||||||
1. For getting started, you can use the [default developer configuration](developer-config.md), which is available out-of-the-box for the MQ Advanced for Developers image
|
1. For getting started, you can use the [default developer configuration](developer-config.md), which is available out-of-the-box for the MQ Advanced for Developers image
|
||||||
2. By creating your own image and adding your own MQSC file into the `/etc/mqm` directory on the image. This file will be run when your queue manager is created.
|
2. By creating your own image and adding your own MQSC file into the `/etc/mqm` directory on the image. This file will be run when your queue manager is created.
|
||||||
3. By using [remote MQ administration](https://www.ibm.com/support/knowledgecenter/SSFKSJ_9.2.0/com.ibm.mq.adm.doc/q021090_.htm), via an MQ command server, the MQ HTTP APIs, or using a tool such as the MQ web console or MQ Explorer.
|
3. By using [remote MQ administration](https://www.ibm.com/support/knowledgecenter/SSFKSJ_9.3.0/com.ibm.mq.adm.doc/q021090_.htm), via an MQ command server, the MQ HTTP APIs, or using a tool such as the MQ web console or MQ Explorer.
|
||||||
|
|
||||||
Note that a listener is always created on port 1414 inside the container. This port can be mapped to any port on the Docker host.
|
Note that a listener is always created on port 1414 inside the container. This port can be mapped to any port on the Docker host.
|
||||||
|
|
||||||
The following is an *example* `Dockerfile` for creating your own pre-configured image, which adds a custom MQ configuration file:
|
The following is an *example* `Dockerfile` for creating your own pre-configured image, which adds a custom MQ configuration file:
|
||||||
|
|
||||||
```dockerfile
|
```dockerfile
|
||||||
FROM ibmcom/mq
|
FROM icr.io/ibm-messaging/mq
|
||||||
USER 1001
|
USER 1001
|
||||||
COPY 20-config.mqsc /etc/mqm/
|
COPY 20-config.mqsc /etc/mqm/
|
||||||
```
|
```
|
||||||
@@ -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.
|
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>')`
|
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.
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,4 +1,4 @@
|
|||||||
* © Copyright IBM Corporation 2019
|
* © Copyright IBM Corporation 2019, 2022
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@@ -16,4 +16,5 @@
|
|||||||
* Set the keystore location for the queue manager
|
* Set the keystore location for the queue manager
|
||||||
ALTER QMGR SSLKEYR('{{ .SSLKeyR }}')
|
ALTER QMGR SSLKEYR('{{ .SSLKeyR }}')
|
||||||
ALTER QMGR CERTLABL('{{ .CertificateLabel }}')
|
ALTER QMGR CERTLABL('{{ .CertificateLabel }}')
|
||||||
|
ALTER QMGR SSLFIPS({{ .SSLFips }})
|
||||||
REFRESH SECURITY(*) TYPE(SSL)
|
REFRESH SECURITY(*) TYPE(SSL)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
ServiceComponent:
|
ServiceComponent:
|
||||||
Service=AuthorizationService
|
Service=AuthorizationService
|
||||||
Name=Dev.HtpAuth.Service
|
Name=Dev.HtpAuth.Service
|
||||||
Module=/opt/mqm/lib64/amqpasdev.so
|
Module=/opt/mqm/lib64/mqhtpass.so
|
||||||
ComponentDataSize=0
|
ComponentDataSize=0
|
||||||
ServiceComponent:
|
ServiceComponent:
|
||||||
Service=AuthorizationService
|
Service=AuthorizationService
|
||||||
|
|||||||
29
go.mod
29
go.mod
@@ -1,17 +1,24 @@
|
|||||||
module github.com/ibm-messaging/mq-container
|
module github.com/ibm-messaging/mq-container
|
||||||
|
|
||||||
go 1.14
|
go 1.19
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/genuinetools/amicontained v0.4.0
|
github.com/genuinetools/amicontained v0.4.3
|
||||||
github.com/genuinetools/pkg v0.0.0-20181022210355-2fcf164d37cb // indirect
|
|
||||||
github.com/ibm-messaging/mq-golang v2.0.0+incompatible
|
github.com/ibm-messaging/mq-golang v2.0.0+incompatible
|
||||||
github.com/prometheus/client_golang v1.7.1
|
github.com/prometheus/client_golang v1.19.1
|
||||||
github.com/prometheus/client_model v0.2.0
|
github.com/prometheus/client_model v0.6.1
|
||||||
github.com/prometheus/common v0.14.0 // indirect
|
golang.org/x/crypto v0.24.0
|
||||||
github.com/prometheus/procfs v0.2.0 // indirect
|
golang.org/x/sys v0.21.0
|
||||||
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 // indirect
|
software.sslmate.com/src/go-pkcs12 v0.4.0
|
||||||
golang.org/x/crypto v0.0.0-20200930160638-afb6bcd081ae
|
)
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f
|
|
||||||
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.2.0 // indirect
|
||||||
|
github.com/golang/protobuf v1.5.3 // indirect
|
||||||
|
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
||||||
|
github.com/prometheus/common v0.48.0 // indirect
|
||||||
|
github.com/prometheus/procfs v0.12.0 // indirect
|
||||||
|
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 // indirect
|
||||||
|
google.golang.org/protobuf v1.33.0 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
344
go.sum
344
go.sum
@@ -1,84 +1,29 @@
|
|||||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
|
||||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
|
||||||
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
|
|
||||||
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
|
|
||||||
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
|
|
||||||
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
|
|
||||||
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
|
|
||||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||||
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
|
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
|
||||||
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
|
||||||
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
|
||||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
|
||||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
|
||||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
|
||||||
github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
|
|
||||||
github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
|
|
||||||
github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
|
||||||
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
|
|
||||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
|
||||||
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
|
|
||||||
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
|
||||||
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
|
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
|
||||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
|
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
|
|
||||||
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
|
|
||||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
|
||||||
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
|
||||||
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
|
||||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
|
||||||
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
|
||||||
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
|
|
||||||
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
|
|
||||||
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
|
|
||||||
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
|
|
||||||
github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
|
|
||||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
|
||||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
|
||||||
github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
|
|
||||||
github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
|
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
|
||||||
github.com/genuinetools/amicontained v0.4.0 h1:J70LMWTebQqQJQaQx9uAW82A6QQqe5ux9GMFgo3NAGY=
|
|
||||||
github.com/genuinetools/amicontained v0.4.0/go.mod h1:PAMZkg9CcUTa6gNyULQ6tOMTMEb2HTKJufvKeFqDw+o=
|
|
||||||
github.com/genuinetools/amicontained v0.4.3 h1:cqq9XiAHfWWY3dk8VU8bSJFu9yh8Il5coEdeTAPq72o=
|
github.com/genuinetools/amicontained v0.4.3 h1:cqq9XiAHfWWY3dk8VU8bSJFu9yh8Il5coEdeTAPq72o=
|
||||||
github.com/genuinetools/amicontained v0.4.3/go.mod h1:PAMZkg9CcUTa6gNyULQ6tOMTMEb2HTKJufvKeFqDw+o=
|
github.com/genuinetools/amicontained v0.4.3/go.mod h1:PAMZkg9CcUTa6gNyULQ6tOMTMEb2HTKJufvKeFqDw+o=
|
||||||
github.com/genuinetools/amicontained v0.4.9 h1:/LvLdgD7iO3IPk7neqfcwB7ufoH7tG77u1pERXBIj7w=
|
|
||||||
github.com/genuinetools/pkg v0.0.0-20181022210355-2fcf164d37cb h1:9MQ4N7zyYTtdjLGqE5McDbgjIjqR5TAPc6lytEOdndc=
|
|
||||||
github.com/genuinetools/pkg v0.0.0-20181022210355-2fcf164d37cb/go.mod h1:XTcrCYlXPxnxL2UpnwuRn7tcaTn9HAhxFoFJucootk8=
|
|
||||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
|
||||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||||
github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o=
|
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
|
||||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||||
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
|
||||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||||
github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
|
|
||||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||||
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
|
||||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
|
||||||
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
|
||||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
|
||||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
|
||||||
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
@@ -87,187 +32,74 @@ github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:x
|
|||||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||||
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
|
|
||||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
|
||||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4=
|
||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||||
|
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
|
|
||||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||||
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
|
||||||
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
|
||||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
|
||||||
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
|
||||||
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
|
||||||
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
|
||||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
|
||||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
|
||||||
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
|
||||||
github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=
|
|
||||||
github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
|
|
||||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
|
||||||
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
|
||||||
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
|
||||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
|
||||||
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
|
|
||||||
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
|
||||||
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
|
||||||
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
|
||||||
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
|
||||||
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
|
||||||
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
|
|
||||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
|
||||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
|
||||||
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
|
||||||
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
|
||||||
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
|
||||||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
|
||||||
github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
|
|
||||||
github.com/ibm-messaging/mq-golang v1.0.0 h1:NZHBQlJzAuNsVv09sooYgxBWPvRUX4L6wZIuOSumiKE=
|
|
||||||
github.com/ibm-messaging/mq-golang v2.0.0+incompatible h1:xAufRPYSzoRGaME2+x7LcW5+uvy/G3xL/3Sn3u+G/lY=
|
github.com/ibm-messaging/mq-golang v2.0.0+incompatible h1:xAufRPYSzoRGaME2+x7LcW5+uvy/G3xL/3Sn3u+G/lY=
|
||||||
github.com/ibm-messaging/mq-golang v2.0.0+incompatible/go.mod h1:qjsZDb7m1oKnbPeDma2JVJTKgyCA91I4bcJ1qHY+gcA=
|
github.com/ibm-messaging/mq-golang v2.0.0+incompatible/go.mod h1:qjsZDb7m1oKnbPeDma2JVJTKgyCA91I4bcJ1qHY+gcA=
|
||||||
github.com/ibm-messaging/mq-golang v3.0.0+incompatible h1:Yc3c8emAyveT54uNDRMkgvS+EBAHeLNWHkc3hk5x+IY=
|
|
||||||
github.com/ibm-messaging/mq-golang v3.0.0+incompatible/go.mod h1:qjsZDb7m1oKnbPeDma2JVJTKgyCA91I4bcJ1qHY+gcA=
|
|
||||||
github.com/ibm-messaging/mq-golang/v5 v5.0.0-alpha h1:Bw2c+k+o9VTMXpiVBmX6PKOm/vPuihx6dO2knPAhkKc=
|
|
||||||
github.com/ibm-messaging/mq-golang/v5 v5.0.0-alpha/go.mod h1:ywCwmYbJOU/E0rl+z4GiNoxVMty68O+LVO39a1VMXrE=
|
|
||||||
github.com/ibm-messaging/mq-golang/v5 v5.1.2 h1:u0e1Vce2TNqJpH088vF77rDMsnMRWnGaOIlxZo4DMZc=
|
|
||||||
github.com/ibm-messaging/mq-golang/v5 v5.1.2/go.mod h1:ywCwmYbJOU/E0rl+z4GiNoxVMty68O+LVO39a1VMXrE=
|
|
||||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
|
||||||
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
|
|
||||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
|
||||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
|
||||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
||||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||||
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
|
||||||
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
|
||||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
|
||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
|
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
|
|
||||||
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
|
|
||||||
github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
|
|
||||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
|
||||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
|
||||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
|
||||||
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
|
||||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
|
||||||
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
|
||||||
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
|
||||||
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
|
||||||
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
|
||||||
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
|
||||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||||
github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
|
|
||||||
github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU=
|
|
||||||
github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k=
|
|
||||||
github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w=
|
|
||||||
github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
|
|
||||||
github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
|
|
||||||
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
|
||||||
github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
|
|
||||||
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
|
|
||||||
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
|
||||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
|
||||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
|
||||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
|
||||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
|
|
||||||
github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis=
|
|
||||||
github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
|
|
||||||
github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
|
||||||
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
|
||||||
github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA=
|
|
||||||
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
|
|
||||||
github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
|
|
||||||
github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
|
|
||||||
github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM=
|
|
||||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
|
||||||
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
|
|
||||||
github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
|
|
||||||
github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
|
|
||||||
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
|
||||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
|
||||||
github.com/prometheus/client_golang v0.8.0 h1:1921Yw9Gc3iSc4VQh3PIoOqgPCZS7G/4xQNVUp8Mda8=
|
|
||||||
github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
|
||||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||||
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
|
|
||||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||||
github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og=
|
|
||||||
github.com/prometheus/client_golang v1.7.1 h1:NTGy1Ja9pByO+xAeH/qiWnLrKtr3hJPNjaVUwnjpdpA=
|
|
||||||
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
|
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
|
||||||
|
github.com/prometheus/client_golang v1.11.1 h1:+4eQaD7vAZ6DsfsxB15hbE0odUjGI5ARs9yskGu1v4s=
|
||||||
|
github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
|
||||||
|
github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE=
|
||||||
|
github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=
|
||||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||||
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
|
||||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
|
||||||
github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
|
||||||
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
|
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
|
||||||
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
|
||||||
|
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
|
||||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||||
github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
|
|
||||||
github.com/prometheus/common v0.10.0 h1:RyRA7RzGXQZiW+tGMr7sxa85G1z0yOpM1qq5c8lNawc=
|
|
||||||
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
||||||
github.com/prometheus/common v0.14.0 h1:RHRyE8UocrbjU+6UvRzwi6HjiDfxrrBU91TtbKzkGp4=
|
github.com/prometheus/common v0.26.0 h1:iMAkS2TDoNWnKM+Kopnx/8tnEStIfpYA0ur0xQzzhMQ=
|
||||||
github.com/prometheus/common v0.14.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s=
|
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
|
||||||
|
github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE=
|
||||||
|
github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
|
||||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||||
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
|
||||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||||
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
|
|
||||||
github.com/prometheus/procfs v0.1.3 h1:F0+tqvhOksq22sc6iCHF5WGlWjdwj92p0udFh1VFBS8=
|
|
||||||
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||||
github.com/prometheus/procfs v0.2.0 h1:wH4vA7pcjKuZzjF7lM8awk4fnuJO6idemZXoKnULUx4=
|
github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4=
|
||||||
github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
|
||||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
|
||||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
|
||||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
|
||||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
|
||||||
github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
|
|
||||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
|
||||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||||
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
|
|
||||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
|
||||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
|
||||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
|
||||||
github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
|
|
||||||
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
|
||||||
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
|
||||||
github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
|
|
||||||
github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
|
|
||||||
github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI=
|
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
@@ -275,153 +107,65 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
|
|||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 h1:kdXcSzyDtseVEc4yCz2qF8ZrQvIDBJLl4S1c3GCXmoI=
|
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 h1:kdXcSzyDtseVEc4yCz2qF8ZrQvIDBJLl4S1c3GCXmoI=
|
||||||
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
|
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
|
||||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
|
||||||
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
|
||||||
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
|
||||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
|
||||||
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
|
||||||
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
|
|
||||||
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
|
||||||
go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
|
||||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
|
||||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
|
||||||
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
|
||||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
|
||||||
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
|
|
||||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
|
|
||||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
|
||||||
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
|
|
||||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
|
||||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20200930160638-afb6bcd081ae h1:duLSQW+DZ5MsXKX7kc4rXlq6/mmxz4G6ewJuBPlhRe0=
|
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY=
|
||||||
golang.org/x/crypto v0.0.0-20200930160638-afb6bcd081ae/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
|
||||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
|
||||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
|
||||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
|
||||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
|
||||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
|
||||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
|
||||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/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-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
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=
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
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-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-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA=
|
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/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/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
|
||||||
|
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
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.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
|
||||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
|
||||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
|
||||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
|
||||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
|
||||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
|
||||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
|
||||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
|
||||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
|
||||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
|
||||||
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
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=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
|
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
|
||||||
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
|
||||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
|
||||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
|
||||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
|
||||||
google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
|
|
||||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
|
||||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
|
||||||
google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM=
|
|
||||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
|
||||||
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
|
||||||
google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
|
||||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
|
||||||
google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
|
||||||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
|
||||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||||
google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
|
|
||||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
|
google.golang.org/protobuf v1.26.0-rc.1 h1:7QnIQpGRHE5RnLKnESfDoxm2dTapTZua5a0kS0A+VXQ=
|
||||||
|
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
|
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
|
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||||
|
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
|
|
||||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
|
||||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
|
||||||
gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
|
|
||||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
|
||||||
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
|
|
||||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
|
||||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
|
||||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
|
||||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
|
||||||
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
|
|
||||||
software.sslmate.com/src/go-pkcs12 v0.0.0-20200830195227-52f69702a001 h1:AVd6O+azYjVQYW1l55IqkbL8/JxjrLtO6q4FCmV8N5c=
|
software.sslmate.com/src/go-pkcs12 v0.0.0-20200830195227-52f69702a001 h1:AVd6O+azYjVQYW1l55IqkbL8/JxjrLtO6q4FCmV8N5c=
|
||||||
software.sslmate.com/src/go-pkcs12 v0.0.0-20200830195227-52f69702a001/go.mod h1:/xvNRWUqm0+/ZMiF4EX00vrSCMsE4/NHb+Pt3freEeQ=
|
software.sslmate.com/src/go-pkcs12 v0.0.0-20200830195227-52f69702a001/go.mod h1:/xvNRWUqm0+/ZMiF4EX00vrSCMsE4/NHb+Pt3freEeQ=
|
||||||
sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
|
software.sslmate.com/src/go-pkcs12 v0.4.0 h1:H2g08FrTvSFKUj+D309j1DPfk5APnIdAQAB8aEykJ5k=
|
||||||
|
software.sslmate.com/src/go-pkcs12 v0.4.0/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI=
|
||||||
|
|||||||
21
ha/native-ha.ini.tpl
Normal file
21
ha/native-ha.ini.tpl
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
NativeHALocalInstance:
|
||||||
|
Name={{ .Name }}
|
||||||
|
{{ if .CertificateLabel }}
|
||||||
|
CertificateLabel={{ .CertificateLabel }}
|
||||||
|
KeyRepository={{ .KeyRepository }}
|
||||||
|
{{ if .CipherSpec }}
|
||||||
|
CipherSpec={{ .CipherSpec }}
|
||||||
|
{{- end }}
|
||||||
|
{{ if .SSLFipsRequired }}
|
||||||
|
SSLFipsRequired={{ .SSLFipsRequired }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
NativeHAInstance:
|
||||||
|
Name={{ .NativeHAInstance0_Name }}
|
||||||
|
ReplicationAddress={{ .NativeHAInstance0_ReplicationAddress }}
|
||||||
|
NativeHAInstance:
|
||||||
|
Name={{ .NativeHAInstance1_Name }}
|
||||||
|
ReplicationAddress={{ .NativeHAInstance1_ReplicationAddress }}
|
||||||
|
NativeHAInstance:
|
||||||
|
Name={{ .NativeHAInstance2_Name }}
|
||||||
|
ReplicationAddress={{ .NativeHAInstance2_ReplicationAddress }}
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
FROM ubuntu:16.04
|
FROM ubuntu:24.04
|
||||||
|
|
||||||
# The URL to download the MQ installer from in tar.gz format
|
# The URL to download the MQ installer from in tar.gz format
|
||||||
ARG MQ_URL=https://public.dhe.ibm.com/ibmdl/export/pub/software/websphere/messaging/mqadv/mqadv_dev911_ubuntu_x86-64.tar.gz
|
ARG MQ_URL=https://public.dhe.ibm.com/ibmdl/export/pub/software/websphere/messaging/mqadv/mqadv_dev911_ubuntu_x86-64.tar.gz
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
* © Copyright IBM Corporation 2018, 2019
|
* © Copyright IBM Corporation 2018, 2022
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@@ -14,5 +14,5 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
|
|
||||||
* Set the cipherspec for dev channels
|
* Set the cipherspec for dev channels
|
||||||
ALTER CHANNEL('DEV.APP.SVRCONN') CHLTYPE(SVRCONN) SSLCIPH(ANY_TLS12) SSLCAUTH(OPTIONAL)
|
ALTER CHANNEL('DEV.APP.SVRCONN') CHLTYPE(SVRCONN) SSLCIPH(ANY_TLS12_OR_HIGHER) SSLCAUTH(OPTIONAL)
|
||||||
ALTER CHANNEL('DEV.ADMIN.SVRCONN') CHLTYPE(SVRCONN) SSLCIPH(ANY_TLS12) SSLCAUTH(OPTIONAL)
|
ALTER CHANNEL('DEV.ADMIN.SVRCONN') CHLTYPE(SVRCONN) SSLCIPH(ANY_TLS12_OR_HIGHER) SSLCAUTH(OPTIONAL)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# -*- mode: sh -*-
|
# -*- mode: sh -*-
|
||||||
# © Copyright IBM Corporation 2019
|
# © Copyright IBM Corporation 2019, 2021
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@@ -25,17 +25,17 @@ test -f /usr/bin/apt-get && UBUNTU=true || UBUNTU=false
|
|||||||
if ($UBUNTU); then
|
if ($UBUNTU); then
|
||||||
export DEBIAN_FRONTEND=noninteractive
|
export DEBIAN_FRONTEND=noninteractive
|
||||||
apt-get update
|
apt-get update
|
||||||
apt-get install -y --no-install-recommends sudo
|
apt-get install -y --no-install-recommends libaprutil1
|
||||||
rm -rf /var/lib/apt/lists/*
|
rm -rf /var/lib/apt/lists/*
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ($YUM); then
|
if ($YUM); then
|
||||||
yum -y install sudo
|
yum -y install apr-util-openssl
|
||||||
yum -y clean all
|
yum -y clean all
|
||||||
rm -rf /var/cache/yum/*
|
rm -rf /var/cache/yum/*
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ($MICRODNF); then
|
if ($MICRODNF); then
|
||||||
microdnf install sudo
|
microdnf --disableplugin=subscription-manager install apr-util-openssl
|
||||||
microdnf clean all
|
microdnf --disableplugin=subscription-manager clean all
|
||||||
fi
|
fi
|
||||||
@@ -36,5 +36,9 @@
|
|||||||
</basicRegistry>
|
</basicRegistry>
|
||||||
<variable name="httpHost" value="*"/>
|
<variable name="httpHost" value="*"/>
|
||||||
<variable name="managementMode" value="externallyprovisioned"/>
|
<variable name="managementMode" value="externallyprovisioned"/>
|
||||||
|
<variable name="mqConsoleRemoteSupportEnabled" value="false"/>
|
||||||
|
<variable name="mqConsoleEnableUnsafeInline" value="true"/>
|
||||||
|
<jndiEntry jndiName="mqConsoleDefaultCCDTHostname" value="${env.MQ_CONSOLE_DEFAULT_CCDT_HOSTNAME}"/>
|
||||||
|
<jndiEntry jndiName="mqConsoleDefaultCCDTPort" value="${env.MQ_CONSOLE_DEFAULT_CCDT_PORT}"/>
|
||||||
<include location="tls.xml"/>
|
<include location="tls.xml"/>
|
||||||
</server>
|
</server>
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ set -ex
|
|||||||
|
|
||||||
sudo curl -Lo /usr/local/bin/dep https://github.com/golang/dep/releases/download/v0.5.1/dep-linux-$ARCH
|
sudo curl -Lo /usr/local/bin/dep https://github.com/golang/dep/releases/download/v0.5.1/dep-linux-$ARCH
|
||||||
sudo chmod +x /usr/local/bin/dep
|
sudo chmod +x /usr/local/bin/dep
|
||||||
|
sudo apt-get update || :
|
||||||
|
sudo apt-get install -y jq
|
||||||
|
|
||||||
go get -u golang.org/x/lint/golint
|
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."
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# -*- mode: sh -*-
|
# -*- mode: sh -*-
|
||||||
# © Copyright IBM Corporation 2015, 2020
|
# © Copyright IBM Corporation 2015, 2023
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@@ -22,6 +22,7 @@ test -f /usr/bin/yum && YUM=true || YUM=false
|
|||||||
test -f /usr/bin/microdnf && MICRODNF=true || MICRODNF=false
|
test -f /usr/bin/microdnf && MICRODNF=true || MICRODNF=false
|
||||||
test -f /usr/bin/rpm && RPM=true || RPM=false
|
test -f /usr/bin/rpm && RPM=true || RPM=false
|
||||||
test -f /usr/bin/apt-get && UBUNTU=true || UBUNTU=false
|
test -f /usr/bin/apt-get && UBUNTU=true || UBUNTU=false
|
||||||
|
CPU_ARCH=$(uname -m)
|
||||||
|
|
||||||
if ($UBUNTU); then
|
if ($UBUNTU); then
|
||||||
export DEBIAN_FRONTEND=noninteractive
|
export DEBIAN_FRONTEND=noninteractive
|
||||||
@@ -29,8 +30,7 @@ if ($UBUNTU); then
|
|||||||
# This ensures no unsupported code gets installed, and makes the build faster
|
# This ensures no unsupported code gets installed, and makes the build faster
|
||||||
source /etc/os-release
|
source /etc/os-release
|
||||||
# Figure out the correct apt URL based on the CPU architecture
|
# Figure out the correct apt URL based on the CPU architecture
|
||||||
CPU_ARCH=$(uname -p)
|
if [ "${CPU_ARCH}" == "x86_64" ]; then
|
||||||
if [ ${CPU_ARCH} == "x86_64" ]; then
|
|
||||||
APT_URL="http://archive.ubuntu.com/ubuntu/"
|
APT_URL="http://archive.ubuntu.com/ubuntu/"
|
||||||
else
|
else
|
||||||
APT_URL="http://ports.ubuntu.com/ubuntu-ports/"
|
APT_URL="http://ports.ubuntu.com/ubuntu-ports/"
|
||||||
@@ -41,32 +41,16 @@ if ($UBUNTU); then
|
|||||||
echo "deb ${APT_URL} ${UBUNTU_CODENAME}-updates main restricted" >> /etc/apt/sources.list
|
echo "deb ${APT_URL} ${UBUNTU_CODENAME}-updates main restricted" >> /etc/apt/sources.list
|
||||||
echo "deb ${APT_URL} ${UBUNTU_CODENAME}-security main restricted" >> /etc/apt/sources.list
|
echo "deb ${APT_URL} ${UBUNTU_CODENAME}-security main restricted" >> /etc/apt/sources.list
|
||||||
# Install additional packages required by MQ, this install process and the runtime scripts
|
# Install additional packages required by MQ, this install process and the runtime scripts
|
||||||
|
EXTRA_DEBS="bash bc ca-certificates coreutils curl debianutils file findutils gawk grep libc-bin mount passwd procps sed tar util-linux"
|
||||||
apt-get update
|
apt-get update
|
||||||
apt-get install -y --no-install-recommends \
|
apt-get install -y --no-install-recommends ${EXTRA_DEBS}
|
||||||
bash \
|
|
||||||
bc \
|
|
||||||
ca-certificates \
|
|
||||||
coreutils \
|
|
||||||
curl \
|
|
||||||
debianutils \
|
|
||||||
file \
|
|
||||||
findutils \
|
|
||||||
gawk \
|
|
||||||
grep \
|
|
||||||
libc-bin \
|
|
||||||
mount \
|
|
||||||
passwd \
|
|
||||||
procps \
|
|
||||||
sed \
|
|
||||||
tar \
|
|
||||||
util-linux
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ($RPM); then
|
if ($RPM); then
|
||||||
EXTRA_RPMS="bash bc ca-certificates file findutils gawk glibc-common grep ncurses-compat-libs passwd procps-ng sed shadow-utils tar util-linux which"
|
EXTRA_RPMS="bash bc ca-certificates file findutils gawk glibc-common grep ncurses-compat-libs passwd procps-ng sed shadow-utils tar util-linux which"
|
||||||
# Install additional packages required by MQ, this install process and the runtime scripts
|
# Install additional packages required by MQ, this install process and the runtime scripts
|
||||||
$YUM && yum -y install --setopt install_weak_deps=false ${EXTRA_RPMS}
|
$YUM && yum -y install --setopt install_weak_deps=false ${EXTRA_RPMS}
|
||||||
$MICRODNF && microdnf install ${EXTRA_RPMS}
|
$MICRODNF && microdnf --disableplugin=subscription-manager install ${EXTRA_RPMS}
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Apply any bug fixes not included in base Ubuntu or MQ image.
|
# Apply any bug fixes not included in base Ubuntu or MQ image.
|
||||||
@@ -78,4 +62,4 @@ $UBUNTU && apt-get install -y libapparmor1 libsystemd0 systemd systemd-sysv libu
|
|||||||
$UBUNTU && rm -rf /var/lib/apt/lists/*
|
$UBUNTU && rm -rf /var/lib/apt/lists/*
|
||||||
$YUM && yum -y clean all
|
$YUM && yum -y clean all
|
||||||
$YUM && rm -rf /var/cache/yum/*
|
$YUM && rm -rf /var/cache/yum/*
|
||||||
$MICRODNF && microdnf clean all
|
$MICRODNF && microdnf --disableplugin=subscription-manager clean all
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
© Copyright IBM Corporation 2017, 2020
|
© Copyright IBM Corporation 2017, 2022
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -18,6 +18,7 @@ limitations under the License.
|
|||||||
package command
|
package command
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
)
|
)
|
||||||
@@ -27,9 +28,13 @@ import (
|
|||||||
// Do not use this function to run shell built-ins (like "cd"), because
|
// Do not use this function to run shell built-ins (like "cd"), because
|
||||||
// the error handling works differently
|
// the error handling works differently
|
||||||
func Run(name string, arg ...string) (string, int, error) {
|
func Run(name string, arg ...string) (string, int, error) {
|
||||||
|
return RunContext(context.Background(), name, arg...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func RunContext(ctx context.Context, name string, arg ...string) (string, int, error) {
|
||||||
// Run the command and wait for completion
|
// Run the command and wait for completion
|
||||||
// #nosec G204
|
// #nosec G204
|
||||||
cmd := exec.Command(name, arg...)
|
cmd := exec.CommandContext(ctx, name, arg...)
|
||||||
out, err := cmd.CombinedOutput()
|
out, err := cmd.CombinedOutput()
|
||||||
rc := cmd.ProcessState.ExitCode()
|
rc := cmd.ProcessState.ExitCode()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -36,12 +36,15 @@ func CopyFileMode(src, dest string, perm os.FileMode) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to open %s for copy: %v", src, err)
|
return fmt.Errorf("failed to open %s for copy: %v", src, err)
|
||||||
}
|
}
|
||||||
|
// #nosec G307 - local to this function, pose no harm.
|
||||||
defer in.Close()
|
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)
|
out, err := os.OpenFile(dest, os.O_CREATE|os.O_WRONLY, perm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to open %s for copy: %v", dest, err)
|
return fmt.Errorf("failed to open %s for copy: %v", dest, err)
|
||||||
}
|
}
|
||||||
|
// #nosec G307 - local to this function, pose no harm.
|
||||||
defer out.Close()
|
defer out.Close()
|
||||||
|
|
||||||
_, err = io.Copy(out, in)
|
_, 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
76
internal/ha/ha.go
Normal file
76
internal/ha/ha.go
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
/*
|
||||||
|
© 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.
|
||||||
|
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 ha contains code for high availability
|
||||||
|
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"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ConfigureNativeHA configures native high availability
|
||||||
|
func ConfigureNativeHA(log *logger.Logger) error {
|
||||||
|
|
||||||
|
file := "/etc/mqm/native-ha.ini"
|
||||||
|
templateFile := file + ".tpl"
|
||||||
|
|
||||||
|
templateMap := map[string]string{}
|
||||||
|
templateMap["Name"] = os.Getenv("HOSTNAME")
|
||||||
|
templateMap["NativeHAInstance0_Name"] = os.Getenv("MQ_NATIVE_HA_INSTANCE_0_NAME")
|
||||||
|
templateMap["NativeHAInstance1_Name"] = os.Getenv("MQ_NATIVE_HA_INSTANCE_1_NAME")
|
||||||
|
templateMap["NativeHAInstance2_Name"] = os.Getenv("MQ_NATIVE_HA_INSTANCE_2_NAME")
|
||||||
|
templateMap["NativeHAInstance0_ReplicationAddress"] = os.Getenv("MQ_NATIVE_HA_INSTANCE_0_REPLICATION_ADDRESS")
|
||||||
|
templateMap["NativeHAInstance1_ReplicationAddress"] = os.Getenv("MQ_NATIVE_HA_INSTANCE_1_REPLICATION_ADDRESS")
|
||||||
|
templateMap["NativeHAInstance2_ReplicationAddress"] = os.Getenv("MQ_NATIVE_HA_INSTANCE_2_REPLICATION_ADDRESS")
|
||||||
|
|
||||||
|
if os.Getenv("MQ_NATIVE_HA_TLS") == "true" {
|
||||||
|
keyLabel, _, _, err := tls.ConfigureHATLSKeystore()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
templateMap["CertificateLabel"] = keyLabel
|
||||||
|
|
||||||
|
keyRepository, ok := os.LookupEnv("MQ_NATIVE_HA_KEY_REPOSITORY")
|
||||||
|
if !ok {
|
||||||
|
keyRepository = "/run/runmqserver/ha/tls/key"
|
||||||
|
}
|
||||||
|
templateMap["KeyRepository"] = keyRepository
|
||||||
|
|
||||||
|
cipherSpec, ok := os.LookupEnv("MQ_NATIVE_HA_CIPHERSPEC")
|
||||||
|
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)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
© Copyright IBM Corporation 2020
|
© Copyright IBM Corporation 2020, 2021
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -108,46 +108,6 @@ func (htpfile mapHtPasswd) updateHtPasswordFile(isTest bool) error {
|
|||||||
if isTest {
|
if isTest {
|
||||||
file = "my.htpasswd"
|
file = "my.htpasswd"
|
||||||
}
|
}
|
||||||
|
// #nosec G306 - its a read by owner/s group, and pose no harm.
|
||||||
return ioutil.WriteFile(file, htpfile.GetBytes(), 0660)
|
return ioutil.WriteFile(file, htpfile.GetBytes(), 0660)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AuthenticateUser verifies if the given user password match with htpasswrd
|
|
||||||
func AuthenticateUser(user string, password string, isTest bool) (bool, bool, error) {
|
|
||||||
passwords := mapHtPasswd(map[string]string{})
|
|
||||||
|
|
||||||
if len(strings.TrimSpace(user)) == 0 || len(strings.TrimSpace(password)) == 0 {
|
|
||||||
return false, false, fmt.Errorf("UserId or Password are empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
err := passwords.ReadHtPasswordFile(isTest)
|
|
||||||
if err != nil {
|
|
||||||
return false, false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
ok := false
|
|
||||||
value, found := passwords[user]
|
|
||||||
|
|
||||||
if !found {
|
|
||||||
return found, ok, fmt.Errorf("User not found in the mq.htpasswd file")
|
|
||||||
}
|
|
||||||
|
|
||||||
err = bcrypt.CompareHashAndPassword([]byte(value), []byte(password))
|
|
||||||
return found, err == nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// ValidateUser validates the given user
|
|
||||||
func ValidateUser(user string, isTest bool) (bool, error) {
|
|
||||||
passwords := mapHtPasswd(map[string]string{})
|
|
||||||
|
|
||||||
if len(strings.TrimSpace(user)) == 0 {
|
|
||||||
return false, fmt.Errorf("Userid is empty for AuthenticateUser")
|
|
||||||
}
|
|
||||||
|
|
||||||
err := passwords.ReadHtPasswordFile(isTest)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
_, found := passwords[strings.TrimSpace(user)]
|
|
||||||
return found, nil
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,62 +0,0 @@
|
|||||||
/*
|
|
||||||
© Copyright IBM Corporation 2020
|
|
||||||
|
|
||||||
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 htpasswd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TestCheckUser verifies Htpassword's use
|
|
||||||
func TestCheckUser(t *testing.T) {
|
|
||||||
err := SetPassword("guest", "guestpw", true)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("htpassword test failed due to error:%s\n", err.Error())
|
|
||||||
}
|
|
||||||
found, ok, err := AuthenticateUser("guest", "guestpw", true)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("htpassword test1 failed as user could not be found:%s\n", err.Error())
|
|
||||||
}
|
|
||||||
if found == false || ok == false {
|
|
||||||
t.Fatalf("htpassword test1 failed as user could not be found:%v, ok:%v\n", found, ok)
|
|
||||||
}
|
|
||||||
|
|
||||||
found, ok, err = AuthenticateUser("myguest", "guestpw", true)
|
|
||||||
if err == nil {
|
|
||||||
t.Fatalf("htpassword test2 failed as no error received for non-existing user\n")
|
|
||||||
}
|
|
||||||
if found == true || ok == true {
|
|
||||||
t.Fatalf("htpassword test2 failed for non-existing user found :%v, ok:%v\n", found, ok)
|
|
||||||
}
|
|
||||||
|
|
||||||
found, ok, err = AuthenticateUser("guest", "guest", true)
|
|
||||||
if err == nil {
|
|
||||||
t.Fatalf("htpassword test3 failed as incorrect password of user did not return error\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
if found == false || ok == true {
|
|
||||||
t.Fatalf("htpassword test3 failed for existing user with incorrect passwored found :%v, ok:%v\n", found, ok)
|
|
||||||
}
|
|
||||||
|
|
||||||
found, err = ValidateUser("guest", true)
|
|
||||||
if err != nil || found == false {
|
|
||||||
t.Fatalf("htpassword test4 failed as user could not be found:%v, ok:%v\n", found, ok)
|
|
||||||
}
|
|
||||||
|
|
||||||
found, err = ValidateUser("myguest", true)
|
|
||||||
if err != nil || found == true {
|
|
||||||
t.Fatalf("htpassword test5 failed as non-existing user returned to be found:%v, ok:%v\n", found, ok)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
guest:$2y$05$ifFP0nCmFed6.m4iB9CHRuHFps2YeeuwopmOvszWt0GRnN59p8qxW
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
© Copyright IBM Corporation 2018, 2020
|
© Copyright IBM Corporation 2018, 2023
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -26,6 +26,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/ibm-messaging/mq-container/internal/command"
|
"github.com/ibm-messaging/mq-container/internal/command"
|
||||||
|
"github.com/ibm-messaging/mq-container/internal/fips"
|
||||||
)
|
)
|
||||||
|
|
||||||
// KeyStore describes information about a keystore file
|
// KeyStore describes information about a keystore file
|
||||||
@@ -34,36 +35,46 @@ type KeyStore struct {
|
|||||||
Password string
|
Password string
|
||||||
keyStoreType string
|
keyStoreType string
|
||||||
command string
|
command string
|
||||||
|
fipsEnabled bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewJKSKeyStore creates a new Java Key Store, managed by the runmqckm command
|
// NewJKSKeyStore creates a new Java Key Store, managed by the runmqckm command
|
||||||
func NewJKSKeyStore(filename, password string) *KeyStore {
|
func NewJKSKeyStore(filename, password string) *KeyStore {
|
||||||
return &KeyStore{
|
keyStore := &KeyStore{
|
||||||
Filename: filename,
|
Filename: filename,
|
||||||
Password: password,
|
Password: password,
|
||||||
keyStoreType: "jks",
|
keyStoreType: "jks",
|
||||||
command: "/opt/mqm/bin/runmqckm",
|
command: "/opt/mqm/bin/runmqckm",
|
||||||
|
fipsEnabled: fips.IsFIPSEnabled(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return keyStore
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCMSKeyStore creates a new MQ CMS Key Store, managed by the runmqakm command
|
// NewCMSKeyStore creates a new MQ CMS Key Store, managed by the runmqakm command
|
||||||
func NewCMSKeyStore(filename, password string) *KeyStore {
|
func NewCMSKeyStore(filename, password string) *KeyStore {
|
||||||
return &KeyStore{
|
keyStore := &KeyStore{
|
||||||
Filename: filename,
|
Filename: filename,
|
||||||
Password: password,
|
Password: password,
|
||||||
keyStoreType: "cms",
|
keyStoreType: "cms",
|
||||||
command: "/opt/mqm/bin/runmqakm",
|
command: "/opt/mqm/bin/runmqakm",
|
||||||
|
fipsEnabled: fips.IsFIPSEnabled(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return keyStore
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPKCS12KeyStore creates a new PKCS12 Key Store, managed by the runmqakm command
|
// NewPKCS12KeyStore creates a new PKCS12 Key Store, managed by the runmqakm command
|
||||||
func NewPKCS12KeyStore(filename, password string) *KeyStore {
|
func NewPKCS12KeyStore(filename, password string) *KeyStore {
|
||||||
return &KeyStore{
|
keyStore := &KeyStore{
|
||||||
Filename: filename,
|
Filename: filename,
|
||||||
Password: password,
|
Password: password,
|
||||||
keyStoreType: "p12",
|
keyStoreType: "p12",
|
||||||
command: "/opt/mqm/bin/runmqakm",
|
command: "/opt/mqm/bin/runmqakm",
|
||||||
|
fipsEnabled: fips.IsFIPSEnabled(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return keyStore
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a key store, if it doesn't already exist
|
// 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
|
// 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 {
|
if err != nil {
|
||||||
return fmt.Errorf("error running \"%v -keydb -create\": %v %s", ks.command, err, out)
|
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)
|
_, err := os.Stat(stashFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if os.IsNotExist(err) {
|
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 {
|
if err != nil {
|
||||||
return fmt.Errorf("error running \"%v -keydb -stashpw\": %v %s", ks.command, err, out)
|
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
|
// Import imports a certificate file in the keystore
|
||||||
func (ks *KeyStore) Import(inputFile, password string) error {
|
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 {
|
if err != nil {
|
||||||
return fmt.Errorf("error running \"%v -cert -import\": %v %s", ks.command, err, out)
|
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
|
// CreateSelfSignedCertificate creates a self-signed certificate in the keystore
|
||||||
func (ks *KeyStore) CreateSelfSignedCertificate(label, dn, hostname string) error {
|
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 {
|
if err != nil {
|
||||||
return fmt.Errorf("error running \"%v -cert -create\": %v %s", ks.command, err, out)
|
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
|
// Add adds a CA certificate to the keystore
|
||||||
func (ks *KeyStore) Add(inputFile, label string) error {
|
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 {
|
if err != nil {
|
||||||
return fmt.Errorf("error running \"%v -cert -add\": %v %s", ks.command, err, out)
|
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
|
// Add adds a CA certificate to the keystore
|
||||||
func (ks *KeyStore) AddNoLabel(inputFile string) error {
|
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 {
|
if err != nil {
|
||||||
return fmt.Errorf("error running \"%v -cert -add\": %v %s", ks.command, err, out)
|
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
|
// GetCertificateLabels returns the labels of all certificates in the key store
|
||||||
func (ks *KeyStore) GetCertificateLabels() ([]string, error) {
|
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 {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error running \"%v -cert -list\": %v %s", ks.command, err, out)
|
return nil, fmt.Errorf("error running \"%v -cert -list\": %v %s", ks.command, err, out)
|
||||||
}
|
}
|
||||||
@@ -207,7 +218,7 @@ func (ks *KeyStore) RenameCertificate(from, to string) error {
|
|||||||
|
|
||||||
// ListAllCertificates Lists all certificates in the keystore
|
// ListAllCertificates Lists all certificates in the keystore
|
||||||
func (ks *KeyStore) ListAllCertificates() ([]string, error) {
|
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 {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error running \"%v -cert -list\": %v %s", ks.command, err, out)
|
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
|
var labels []string
|
||||||
for scanner.Scan() {
|
for scanner.Scan() {
|
||||||
s := scanner.Text()
|
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, "!") {
|
if strings.HasPrefix(s, "-") || strings.HasPrefix(s, "*-") || strings.HasPrefix(s, "!") {
|
||||||
s := strings.TrimLeft(s, "-*!")
|
s := strings.TrimLeft(s, "-*!")
|
||||||
labels = append(labels, strings.TrimSpace(s))
|
labels = append(labels, strings.TrimSpace(s))
|
||||||
@@ -226,3 +239,22 @@ func (ks *KeyStore) ListAllCertificates() ([]string, error) {
|
|||||||
}
|
}
|
||||||
return labels, nil
|
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, 2019
|
© Copyright IBM Corporation 2018, 2023
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -35,6 +35,8 @@ const (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
metricsEnabled = false
|
metricsEnabled = false
|
||||||
|
// #nosec G112 - this code is changing soon to use https.
|
||||||
|
// for now we will ignore the gosec.
|
||||||
metricsServer = &http.Server{Addr: ":" + defaultPort}
|
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
|
// If running in standby mode - wait until the queue manager becomes active
|
||||||
for {
|
for {
|
||||||
active, _ := ready.IsRunningAsActiveQM(qmName)
|
status, _ := ready.Status(context.Background(), qmName)
|
||||||
if active {
|
if status.ActiveQM() {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
time.Sleep(requestTimeout * time.Second)
|
time.Sleep(requestTimeout * time.Second)
|
||||||
|
|||||||
@@ -48,8 +48,10 @@ func ProcessTemplateFile(templateFile, destFile string, data interface{}, log *l
|
|||||||
return err
|
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)
|
f, err := os.OpenFile(destFile, os.O_CREATE|os.O_WRONLY, 0660)
|
||||||
|
// #nosec G307 - local to this function, pose no harm.
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
err = t.Execute(f, data)
|
err = t.Execute(f, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ package mqversion
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/ibm-messaging/mq-container/internal/command"
|
"github.com/ibm-messaging/mq-container/internal/command"
|
||||||
@@ -38,14 +39,59 @@ func Compare(checkVersion string) (int, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
// trim any suffix from MQ version x.x.x.x
|
|
||||||
currentVersion = currentVersion[0:7]
|
currentVRMF, err := parseVRMF(currentVersion)
|
||||||
if currentVersion < checkVersion {
|
if err != nil {
|
||||||
return -1, nil
|
return 0, err
|
||||||
} else if currentVersion == checkVersion {
|
|
||||||
return 0, nil
|
|
||||||
} else if currentVersion > checkVersion {
|
|
||||||
return 1, nil
|
|
||||||
}
|
}
|
||||||
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
|
package mqversion
|
||||||
|
|
||||||
import "testing"
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
func TestCompareLower(t *testing.T) {
|
func TestCompareLower(t *testing.T) {
|
||||||
checkVersion := "9.9.9.9"
|
checkVersion := "99.99.99.99"
|
||||||
mqVersionCheck, err := Compare(checkVersion)
|
mqVersionCheck, err := Compare(checkVersion)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to compare MQ versions: %v", err)
|
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)
|
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,299 +0,0 @@
|
|||||||
/*
|
|
||||||
© Copyright IBM Corporation 2020
|
|
||||||
|
|
||||||
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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
//This is a developer only configuration and not recommended for production usage.
|
|
||||||
package main
|
|
||||||
|
|
||||||
/*
|
|
||||||
#cgo !windows CFLAGS: -I/opt/mqm/lib64 -D_REENTRANT
|
|
||||||
#cgo !windows,!darwin LDFLAGS: -L/opt/mqm/lib64 -lmqm_r -Wl,-rpath,/opt/mqm/lib64 -Wl,-rpath,/usr/lib64
|
|
||||||
#cgo darwin LDFLAGS: -L/opt/mqm/lib64 -lmqm_r -Wl,-rpath,/opt/mqm/lib64 -Wl,-rpath,/usr/lib64
|
|
||||||
#cgo windows CFLAGS: -I"C:/Program Files/IBM/MQ/Tools/c/include"
|
|
||||||
#cgo windows LDFLAGS: -L "C:/Program Files/IBM/MQ/bin64" -lmqm
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <cmqc.h>
|
|
||||||
#include <cmqxc.h>
|
|
||||||
#include <cmqzc.h>
|
|
||||||
#include <cmqec.h>
|
|
||||||
#include <time.h>
|
|
||||||
static MQZ_INIT_AUTHORITY PASStart;
|
|
||||||
static MQZ_AUTHENTICATE_USER OAAuthUser;
|
|
||||||
static MQZ_FREE_USER OAFreeUser;
|
|
||||||
static MQZ_TERM_AUTHORITY OATermAuth;
|
|
||||||
extern int Authenticate(char *, char *);
|
|
||||||
extern int CheckAuthority(char *);
|
|
||||||
static char *OAEnvStr(MQLONG);
|
|
||||||
static void FindSize();
|
|
||||||
static void PrintDateTime();
|
|
||||||
static FILE *fp = NULL;
|
|
||||||
static int primary_process = 0;
|
|
||||||
|
|
||||||
static void MQENTRY PASStart(
|
|
||||||
MQHCONFIG hc,
|
|
||||||
MQLONG Options,
|
|
||||||
MQCHAR48 QMgrName,
|
|
||||||
MQLONG ComponentDataLength,
|
|
||||||
PMQBYTE ComponentData,
|
|
||||||
PMQLONG Version,
|
|
||||||
PMQLONG pCompCode,
|
|
||||||
PMQLONG pReason) {
|
|
||||||
MQLONG CC = MQCC_OK;
|
|
||||||
MQLONG Reason = MQRC_NONE;
|
|
||||||
|
|
||||||
if ((Options & MQZIO_PRIMARY) == MQZIO_PRIMARY)
|
|
||||||
primary_process = 1;
|
|
||||||
|
|
||||||
fp=fopen("/var/mqm/errors/amqpasdev.log","a");
|
|
||||||
|
|
||||||
if (CC == MQCC_OK)
|
|
||||||
hc->MQZEP_Call(hc, MQZID_INIT_AUTHORITY,(PMQFUNC)PASStart,&CC,&Reason);
|
|
||||||
|
|
||||||
if (CC == MQCC_OK)
|
|
||||||
hc->MQZEP_Call(hc,MQZID_TERM_AUTHORITY,(PMQFUNC)OATermAuth,&CC,&Reason);
|
|
||||||
|
|
||||||
if (CC == MQCC_OK)
|
|
||||||
hc->MQZEP_Call(hc,MQZID_AUTHENTICATE_USER,(PMQFUNC)OAAuthUser,&CC,&Reason);
|
|
||||||
|
|
||||||
if (CC == MQCC_OK)
|
|
||||||
hc->MQZEP_Call(hc,MQZID_FREE_USER,(PMQFUNC)OAFreeUser,&CC,&Reason);
|
|
||||||
|
|
||||||
*Version = MQZAS_VERSION_5;
|
|
||||||
*pCompCode = CC;
|
|
||||||
*pReason = Reason;
|
|
||||||
|
|
||||||
PrintDateTime();
|
|
||||||
fprintf(fp, "Pluggable OAM Initialized.\n");
|
|
||||||
fprintf(fp, "THIS IS A DEVELOPER ONLY CONFIGURATION AND NOT RECOMMENDED FOR PRODUCTION USAGE");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
static char *authuserfmt =
|
|
||||||
"\tUser : \"%12.12s\"\n"\
|
|
||||||
"\tEffUser : \"%12.12s\"\n"\
|
|
||||||
"\tAppName : \"%28.28s\"\n"\
|
|
||||||
"\tApIdDt : \"%32.32s\"\n"\
|
|
||||||
"\tEnv : \"%s\"\n"\
|
|
||||||
"\tApp Pid : %d\n"\
|
|
||||||
"\tApp Tid : %d\n"\
|
|
||||||
;
|
|
||||||
static void MQENTRY OAAuthUser (
|
|
||||||
PMQCHAR pQMgrName,
|
|
||||||
PMQCSP pSecurityParms,
|
|
||||||
PMQZAC pApplicationContext,
|
|
||||||
PMQZIC pIdentityContext,
|
|
||||||
PMQPTR pCorrelationPtr,
|
|
||||||
PMQBYTE pComponentData,
|
|
||||||
PMQLONG pContinuation,
|
|
||||||
PMQLONG pCompCode,
|
|
||||||
PMQLONG pReason)
|
|
||||||
{
|
|
||||||
char *spuser = NULL;
|
|
||||||
char *sppass = NULL;
|
|
||||||
int gorc = MQRC_NOT_AUTHORIZED;
|
|
||||||
|
|
||||||
if ((pSecurityParms->CSPUserIdLength) > 0) {
|
|
||||||
//Grab the user creds from csp.
|
|
||||||
spuser = malloc(pSecurityParms->CSPUserIdLength+1);
|
|
||||||
strncpy(spuser,pSecurityParms->CSPUserIdPtr,pSecurityParms->CSPUserIdLength);
|
|
||||||
spuser[pSecurityParms->CSPUserIdLength]=0;
|
|
||||||
sppass = malloc(pSecurityParms->CSPPasswordLength+1);
|
|
||||||
strncpy(sppass,pSecurityParms->CSPPasswordPtr,pSecurityParms->CSPPasswordLength);
|
|
||||||
sppass[pSecurityParms->CSPPasswordLength]=0;
|
|
||||||
gorc = Authenticate(spuser,sppass);
|
|
||||||
|
|
||||||
if (gorc == MQRC_NONE) {
|
|
||||||
*pCompCode = MQCC_OK;
|
|
||||||
*pReason = MQRC_NONE;
|
|
||||||
*pContinuation = MQZCI_CONTINUE;
|
|
||||||
memcpy( pIdentityContext->UserIdentifier
|
|
||||||
, spuser
|
|
||||||
, sizeof(pIdentityContext->UserIdentifier) );
|
|
||||||
} else {
|
|
||||||
*pCompCode = MQCC_WARNING;
|
|
||||||
*pReason = MQRC_NONE;
|
|
||||||
*pContinuation = MQZCI_CONTINUE;
|
|
||||||
//we print to error file only if error'd
|
|
||||||
PrintDateTime();
|
|
||||||
if (fp) {
|
|
||||||
fprintf(fp, authuserfmt,
|
|
||||||
pIdentityContext->UserIdentifier,
|
|
||||||
pApplicationContext->EffectiveUserID,
|
|
||||||
pApplicationContext->ApplName,
|
|
||||||
pIdentityContext->ApplIdentityData,
|
|
||||||
OAEnvStr(pApplicationContext->Environment),
|
|
||||||
pApplicationContext->ProcessId,
|
|
||||||
pApplicationContext->ThreadId);
|
|
||||||
|
|
||||||
fprintf(fp,"\tCSP UserId : %s\n", spuser);
|
|
||||||
fprintf(fp,"\tCSP Password : %s\n", "****..");
|
|
||||||
fprintf(fp,"\tPAS-Compcode:%d\n",*pCompCode);
|
|
||||||
fprintf(fp,"\tPAS-Reasoncode:%d\n",*pReason);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
free(spuser);
|
|
||||||
free(sppass);
|
|
||||||
} else {
|
|
||||||
//this is only a normal UID authentication.
|
|
||||||
spuser = malloc(sizeof(PMQCHAR12));
|
|
||||||
strncpy(spuser,pApplicationContext->EffectiveUserID,strlen(pApplicationContext->EffectiveUserID));
|
|
||||||
spuser[sizeof(PMQCHAR12)]=0;
|
|
||||||
gorc = CheckAuthority(spuser);
|
|
||||||
if (gorc == MQRC_NONE){
|
|
||||||
*pCompCode = MQCC_OK;
|
|
||||||
*pReason = MQRC_NONE;
|
|
||||||
*pContinuation = MQZCI_CONTINUE;
|
|
||||||
memcpy( pIdentityContext->UserIdentifier
|
|
||||||
, spuser
|
|
||||||
, sizeof(pIdentityContext->UserIdentifier) );
|
|
||||||
} else {
|
|
||||||
*pCompCode = MQCC_WARNING;
|
|
||||||
*pReason = MQRC_NONE;
|
|
||||||
*pContinuation = MQZCI_CONTINUE;
|
|
||||||
//we print only if error'd
|
|
||||||
PrintDateTime();
|
|
||||||
if (fp)
|
|
||||||
{
|
|
||||||
fprintf(fp, authuserfmt,
|
|
||||||
pIdentityContext->UserIdentifier,
|
|
||||||
pApplicationContext->EffectiveUserID,
|
|
||||||
pApplicationContext->ApplName,
|
|
||||||
pIdentityContext->ApplIdentityData,
|
|
||||||
OAEnvStr(pApplicationContext->Environment),
|
|
||||||
pApplicationContext->ProcessId,
|
|
||||||
pApplicationContext->ThreadId
|
|
||||||
);
|
|
||||||
fprintf(fp,"\tUID : %s\n", spuser);
|
|
||||||
fprintf(fp,"\tPAS-Compcode:%d\n",*pCompCode);
|
|
||||||
fprintf(fp,"\tPAS-Reasoncode:%d\n",*pReason);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void MQENTRY OAFreeUser (
|
|
||||||
PMQCHAR pQMgrName,
|
|
||||||
PMQZFP pFreeParms,
|
|
||||||
PMQBYTE pComponentData,
|
|
||||||
PMQLONG pContinuation,
|
|
||||||
|
|
||||||
PMQLONG pCompCode,
|
|
||||||
PMQLONG pReason)
|
|
||||||
{
|
|
||||||
*pCompCode = MQCC_WARNING;
|
|
||||||
*pReason = MQRC_NONE;
|
|
||||||
*pContinuation = MQZCI_CONTINUE;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void MQENTRY OATermAuth(
|
|
||||||
MQHCONFIG hc,
|
|
||||||
MQLONG Options,
|
|
||||||
PMQCHAR pQMgrName,
|
|
||||||
PMQBYTE pComponentData,
|
|
||||||
PMQLONG pCompCode,
|
|
||||||
PMQLONG pReason)
|
|
||||||
{
|
|
||||||
if ((primary_process) && ((Options & MQZTO_PRIMARY) == MQZTO_PRIMARY) ||
|
|
||||||
((Options & MQZTO_SECONDARY) == MQZTO_SECONDARY))
|
|
||||||
{
|
|
||||||
if (fp)
|
|
||||||
{
|
|
||||||
fclose(fp);
|
|
||||||
fp = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*pCompCode = MQCC_OK;
|
|
||||||
*pReason = MQRC_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void PrintDateTime() {
|
|
||||||
FindSize();
|
|
||||||
struct tm *local;
|
|
||||||
time_t t;
|
|
||||||
t = time(NULL);
|
|
||||||
local = localtime(&t);
|
|
||||||
if (fp) {
|
|
||||||
fprintf(fp, "-------------------------------------------------\n");
|
|
||||||
fprintf(fp, "Local time: %s", asctime(local));
|
|
||||||
local = gmtime(&t);
|
|
||||||
fprintf(fp, "UTC time: %s", asctime(local));
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
static char *OAEnvStr(MQLONG x)
|
|
||||||
{
|
|
||||||
switch (x)
|
|
||||||
{
|
|
||||||
case MQXE_OTHER: return "Application";
|
|
||||||
case MQXE_MCA: return "Channel";
|
|
||||||
case MQXE_MCA_SVRCONN: return "Channel SvrConn";
|
|
||||||
case MQXE_COMMAND_SERVER: return "Command Server";
|
|
||||||
case MQXE_MQSC: return "MQSC";
|
|
||||||
default: return "Invalid Environment";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void FindSize()
|
|
||||||
{
|
|
||||||
int sz = 0;
|
|
||||||
int prev=ftell(fp);
|
|
||||||
fseek(fp, 0L, SEEK_END);
|
|
||||||
sz=ftell(fp);
|
|
||||||
//if log file size goes over 1mb, rewind it.
|
|
||||||
if (sz > 1000000) {
|
|
||||||
rewind(fp);
|
|
||||||
} else {
|
|
||||||
fseek(fp, prev, SEEK_SET);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
*/
|
|
||||||
import "C"
|
|
||||||
import "github.com/ibm-messaging/mq-container/internal/htpasswd"
|
|
||||||
|
|
||||||
//export MQStart
|
|
||||||
func MQStart(hc C.MQHCONFIG, Options C.MQLONG, QMgrName C.PMQCHAR, ComponentDataLength C.MQLONG, ComponentData C.PMQBYTE, Version C.PMQLONG, pCompCode C.PMQLONG, pReason C.PMQLONG) {
|
|
||||||
C.PASStart(hc, Options, QMgrName, ComponentDataLength, ComponentData, Version, pCompCode, pReason)
|
|
||||||
}
|
|
||||||
|
|
||||||
//export Authenticate
|
|
||||||
func Authenticate(x *C.char, y *C.char) C.int {
|
|
||||||
user := C.GoString(x)
|
|
||||||
pwd := C.GoString(y)
|
|
||||||
found, ok, err := htpasswd.AuthenticateUser(user, pwd, false)
|
|
||||||
|
|
||||||
if !found || !ok || err != nil {
|
|
||||||
return C.MQRC_UNKNOWN_OBJECT_NAME
|
|
||||||
}
|
|
||||||
return C.MQRC_NONE
|
|
||||||
}
|
|
||||||
|
|
||||||
//export CheckAuthority
|
|
||||||
func CheckAuthority(x *C.char) C.int {
|
|
||||||
user := C.GoString(x)
|
|
||||||
found, err := htpasswd.ValidateUser(user, false)
|
|
||||||
if !found || err != nil {
|
|
||||||
return C.MQRC_UNKNOWN_OBJECT_NAME
|
|
||||||
}
|
|
||||||
return C.MQRC_NONE
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {}
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
© Copyright IBM Corporation 2018, 2019
|
© Copyright IBM Corporation 2018, 2023
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -18,6 +18,7 @@ limitations under the License.
|
|||||||
package ready
|
package ready
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -53,6 +54,7 @@ func Clear() error {
|
|||||||
// Set lets any subsequent calls to `CheckReady` know that the queue
|
// Set lets any subsequent calls to `CheckReady` know that the queue
|
||||||
// manager has finished its configuration step
|
// manager has finished its configuration step
|
||||||
func Set() error {
|
func Set() error {
|
||||||
|
// #nosec G306 - this gives permissions to owner/s group only.
|
||||||
return ioutil.WriteFile(fileName, []byte("1"), 0770)
|
return ioutil.WriteFile(fileName, []byte("1"), 0770)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,23 +68,38 @@ func Check() (bool, error) {
|
|||||||
return exists, nil
|
return exists, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsRunningAsActiveQM returns true if the queue manager is running in active mode
|
// Status returns an enum representing the current running status of the queue manager
|
||||||
func IsRunningAsActiveQM(name string) (bool, error) {
|
func Status(ctx context.Context, name string) (QMStatus, error) {
|
||||||
return isRunningQM(name, "(RUNNING)")
|
out, _, err := command.RunContext(ctx, "dspmq", "-n", "-m", name)
|
||||||
}
|
|
||||||
|
|
||||||
// IsRunningAsStandbyQM returns true if the queue manager is running in standby mode
|
|
||||||
func IsRunningAsStandbyQM(name string) (bool, error) {
|
|
||||||
return isRunningQM(name, "(RUNNING AS STANDBY)")
|
|
||||||
}
|
|
||||||
|
|
||||||
func isRunningQM(name string, status string) (bool, error) {
|
|
||||||
out, _, err := command.Run("dspmq", "-n", "-m", name)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return StatusUnknown, err
|
||||||
}
|
}
|
||||||
if strings.Contains(string(out), status) {
|
if strings.Contains(string(out), "(RUNNING)") {
|
||||||
return true, nil
|
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
|
© Copyright IBM Corporation 2019, 2023
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -43,14 +43,20 @@ const cmsKeystoreName = "key.kdb"
|
|||||||
// p12TruststoreName is the name of the PKCS#12 Truststore
|
// p12TruststoreName is the name of the PKCS#12 Truststore
|
||||||
const p12TruststoreName = "trust.p12"
|
const p12TruststoreName = "trust.p12"
|
||||||
|
|
||||||
// keystoreDir is the location for the CMS Keystore & PKCS#12 Truststore
|
// keystoreDirDefault is the location for the default CMS Keystore & PKCS#12 Truststore
|
||||||
const keystoreDir = "/run/runmqserver/tls/"
|
const keystoreDirDefault = "/run/runmqserver/tls/"
|
||||||
|
|
||||||
// keyDir is the location of the keys to import
|
// keystoreDirHA is the location for the HA CMS Keystore
|
||||||
const keyDir = "/etc/mqm/pki/keys"
|
const keystoreDirHA = "/run/runmqserver/ha/tls/"
|
||||||
|
|
||||||
// trustDir is the location of the trust certificates to import
|
// keyDirDefault is the location of the default keys to import
|
||||||
const trustDir = "/etc/mqm/pki/trust"
|
const keyDirDefault = "/etc/mqm/pki/keys"
|
||||||
|
|
||||||
|
// keyDirHA is the location of the HA keys to import
|
||||||
|
const keyDirHA = "/etc/mqm/ha/pki/keys"
|
||||||
|
|
||||||
|
// trustDirDefault is the location of the trust certificates to import
|
||||||
|
const trustDirDefault = "/etc/mqm/pki/trust"
|
||||||
|
|
||||||
type KeyStoreData struct {
|
type KeyStoreData struct {
|
||||||
Keystore *keystore.KeyStore
|
Keystore *keystore.KeyStore
|
||||||
@@ -65,28 +71,45 @@ type P12KeyFiles struct {
|
|||||||
Password string
|
Password string
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConfigureTLSKeystores configures the CMS Keystore & PKCS#12 Truststore
|
type TLSStore struct {
|
||||||
func ConfigureTLSKeystores() (string, KeyStoreData, KeyStoreData, error) {
|
Keystore KeyStoreData
|
||||||
|
Truststore KeyStoreData
|
||||||
|
}
|
||||||
|
|
||||||
// Create the CMS Keystore & PKCS#12 Truststore
|
func configureTLSKeystores(keystoreDir, keyDir, trustDir string, p12TruststoreRequired bool, nativeTLSHA bool) (string, KeyStoreData, KeyStoreData, error) {
|
||||||
cmsKeystore, p12Truststore, err := generateAllKeystores()
|
var keyLabel string
|
||||||
|
// Create the CMS Keystore & PKCS#12 Truststore (if required)
|
||||||
|
tlsStore, err := generateAllKeystores(keystoreDir, p12TruststoreRequired, nativeTLSHA)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", cmsKeystore, p12Truststore, err
|
return "", tlsStore.Keystore, tlsStore.Truststore, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if tlsStore.Keystore.Keystore != nil {
|
||||||
// Process all keys - add them to the CMS KeyStore
|
// Process all keys - add them to the CMS KeyStore
|
||||||
keyLabel, err := processKeys(&cmsKeystore, &p12Truststore)
|
keyLabel, err = processKeys(&tlsStore, keystoreDir, keyDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", cmsKeystore, p12Truststore, err
|
return "", tlsStore.Keystore, tlsStore.Truststore, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process all trust certificates - add them to the CMS KeyStore & PKCS#12 Truststore
|
// Process all trust certificates - add them to the CMS KeyStore & PKCS#12 Truststore (if required)
|
||||||
err = processTrustCertificates(&cmsKeystore, &p12Truststore)
|
err = processTrustCertificates(&tlsStore, trustDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", cmsKeystore, p12Truststore, err
|
return "", tlsStore.Keystore, tlsStore.Truststore, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return keyLabel, cmsKeystore, p12Truststore, err
|
return keyLabel, tlsStore.Keystore, tlsStore.Truststore, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConfigureDefaultTLSKeystores configures the CMS Keystore & PKCS#12 Truststore
|
||||||
|
func ConfigureDefaultTLSKeystores() (string, KeyStoreData, KeyStoreData, error) {
|
||||||
|
return configureTLSKeystores(keystoreDirDefault, keyDirDefault, trustDirDefault, true, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConfigureHATLSKeystore configures the CMS Keystore & PKCS#12 Truststore
|
||||||
|
func ConfigureHATLSKeystore() (string, KeyStoreData, KeyStoreData, error) {
|
||||||
|
// *.crt files mounted to the HA TLS dir keyDirHA will be processed as trusted in the CMS keystore
|
||||||
|
return configureTLSKeystores(keystoreDirHA, keyDirHA, keyDirHA, false, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConfigureTLS configures TLS for the queue manager
|
// ConfigureTLS configures TLS for the queue manager
|
||||||
@@ -94,10 +117,26 @@ func ConfigureTLS(keyLabel string, cmsKeystore KeyStoreData, devMode bool, log *
|
|||||||
|
|
||||||
const mqsc string = "/etc/mqm/15-tls.mqsc"
|
const mqsc string = "/etc/mqm/15-tls.mqsc"
|
||||||
const mqscTemplate string = mqsc + ".tpl"
|
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 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{
|
err := mqtemplate.ProcessTemplateFile(mqscTemplate, mqsc, map[string]string{
|
||||||
"SSLKeyR": strings.TrimSuffix(cmsKeystore.Keystore.Filename, ".kdb"),
|
"SSLKeyR": sslKeyRing,
|
||||||
"CertificateLabel": keyLabel,
|
"CertificateLabel": keyLabel,
|
||||||
|
"SSLFips": fipsEnabled,
|
||||||
}, log)
|
}, log)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -137,8 +176,9 @@ func configureTLSDev(log *logger.Logger) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// generateAllKeystores creates the CMS Keystore & PKCS#12 Truststore
|
// generateAllKeystores creates the CMS Keystore & PKCS#12 Truststore (if required)
|
||||||
func generateAllKeystores() (KeyStoreData, KeyStoreData, error) {
|
func generateAllKeystores(keystoreDir string, p12TruststoreRequired bool, nativeTLSHA bool) (TLSStore, error) {
|
||||||
|
|
||||||
var cmsKeystore, p12Truststore KeyStoreData
|
var cmsKeystore, p12Truststore KeyStoreData
|
||||||
|
|
||||||
// Generate a pasword for use with both the CMS Keystore & PKCS#12 Truststore
|
// Generate a pasword for use with both the CMS Keystore & PKCS#12 Truststore
|
||||||
@@ -150,28 +190,38 @@ func generateAllKeystores() (KeyStoreData, KeyStoreData, error) {
|
|||||||
// #nosec G301 - write group permissions are required
|
// #nosec G301 - write group permissions are required
|
||||||
err := os.MkdirAll(keystoreDir, 0770)
|
err := os.MkdirAll(keystoreDir, 0770)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cmsKeystore, p12Truststore, fmt.Errorf("Failed to create Keystore directory: %v", err)
|
return TLSStore{cmsKeystore, p12Truststore}, fmt.Errorf("Failed to create Keystore directory: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the CMS Keystore
|
// Search the default keys directory for any keys/certs.
|
||||||
|
keysDirectory := keyDirDefault
|
||||||
|
// Change to default native HA TLS directory if we are configuring nativeHA
|
||||||
|
if nativeTLSHA {
|
||||||
|
keysDirectory = keyDirHA
|
||||||
|
}
|
||||||
|
// Create the CMS Keystore if we have been provided keys and certificates
|
||||||
|
if haveKeysAndCerts(keysDirectory) || haveKeysAndCerts(trustDirDefault) {
|
||||||
cmsKeystore.Keystore = keystore.NewCMSKeyStore(filepath.Join(keystoreDir, cmsKeystoreName), cmsKeystore.Password)
|
cmsKeystore.Keystore = keystore.NewCMSKeyStore(filepath.Join(keystoreDir, cmsKeystoreName), cmsKeystore.Password)
|
||||||
err = cmsKeystore.Keystore.Create()
|
err = cmsKeystore.Keystore.Create()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cmsKeystore, p12Truststore, fmt.Errorf("Failed to create CMS Keystore: %v", err)
|
return TLSStore{cmsKeystore, p12Truststore}, fmt.Errorf("Failed to create CMS Keystore: %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the PKCS#12 Truststore
|
// Create the PKCS#12 Truststore (if required)
|
||||||
|
if p12TruststoreRequired {
|
||||||
p12Truststore.Keystore = keystore.NewPKCS12KeyStore(filepath.Join(keystoreDir, p12TruststoreName), p12Truststore.Password)
|
p12Truststore.Keystore = keystore.NewPKCS12KeyStore(filepath.Join(keystoreDir, p12TruststoreName), p12Truststore.Password)
|
||||||
err = p12Truststore.Keystore.Create()
|
err = p12Truststore.Keystore.Create()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cmsKeystore, p12Truststore, fmt.Errorf("Failed to create PKCS#12 Truststore: %v", err)
|
return TLSStore{cmsKeystore, p12Truststore}, fmt.Errorf("Failed to create PKCS#12 Truststore: %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return cmsKeystore, p12Truststore, nil
|
return TLSStore{cmsKeystore, p12Truststore}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// processKeys processes all keys - adding them to the CMS KeyStore
|
// processKeys processes all keys - adding them to the CMS KeyStore
|
||||||
func processKeys(cmsKeystore, p12Truststore *KeyStoreData) (string, error) {
|
func processKeys(tlsStore *TLSStore, keystoreDir string, keyDir string) (string, error) {
|
||||||
|
|
||||||
// Key label - will be set to the label of the first set of keys
|
// Key label - will be set to the label of the first set of keys
|
||||||
keyLabel := ""
|
keyLabel := ""
|
||||||
@@ -179,7 +229,6 @@ func processKeys(cmsKeystore, p12Truststore *KeyStoreData) (string, error) {
|
|||||||
// Process all keys
|
// Process all keys
|
||||||
keyList, err := ioutil.ReadDir(keyDir)
|
keyList, err := ioutil.ReadDir(keyDir)
|
||||||
if err == nil && len(keyList) > 0 {
|
if err == nil && len(keyList) > 0 {
|
||||||
|
|
||||||
// Process each set of keys - each set should contain files: *.key & *.crt
|
// Process each set of keys - each set should contain files: *.key & *.crt
|
||||||
for _, keySet := range keyList {
|
for _, keySet := range keyList {
|
||||||
keys, _ := ioutil.ReadDir(filepath.Join(keyDir, keySet.Name()))
|
keys, _ := ioutil.ReadDir(filepath.Join(keyDir, keySet.Name()))
|
||||||
@@ -190,7 +239,7 @@ func processKeys(cmsKeystore, p12Truststore *KeyStoreData) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Process private key (*.key)
|
// Process private key (*.key)
|
||||||
privateKey, keyPrefix, err := processPrivateKey(keySet.Name(), keys)
|
privateKey, keyPrefix, err := processPrivateKey(keyDir, keySet.Name(), keys)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@@ -201,29 +250,37 @@ func processKeys(cmsKeystore, p12Truststore *KeyStoreData) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Process certificates (*.crt) - public certificate & optional CA certificate
|
// Process certificates (*.crt) - public certificate & optional CA certificate
|
||||||
publicCertificate, caCertificate, err := processCertificates(keySet.Name(), keyPrefix, keys, cmsKeystore, p12Truststore)
|
publicCertificate, caCertificate, err := processCertificates(keyDir, keySet.Name(), keyPrefix, keys, &tlsStore.Keystore, &tlsStore.Truststore)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
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
|
// Create a new PKCS#12 Keystore - containing private key, public certificate & optional CA certificate
|
||||||
file, err := pkcs.Encode(rand.Reader, privateKey, publicCertificate, caCertificate, cmsKeystore.Password)
|
file, err := pkcs.Encode(rand.Reader, privateKey, publicCertificate, caCertificate, tlsStore.Keystore.Password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("Failed to encode PKCS#12 Keystore %s: %v", keySet.Name()+".p12", err)
|
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)
|
err = ioutil.WriteFile(filepath.Join(keystoreDir, keySet.Name()+".p12"), file, 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("Failed to write PKCS#12 Keystore %s: %v", filepath.Join(keystoreDir, keySet.Name()+".p12"), err)
|
return "", fmt.Errorf("Failed to write PKCS#12 Keystore %s: %v", filepath.Join(keystoreDir, keySet.Name()+".p12"), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Import the new PKCS#12 Keystore into the CMS Keystore
|
// Import the new PKCS#12 Keystore into the CMS Keystore
|
||||||
err = cmsKeystore.Keystore.Import(filepath.Join(keystoreDir, keySet.Name()+".p12"), cmsKeystore.Password)
|
err = tlsStore.Keystore.Keystore.Import(filepath.Join(keystoreDir, keySet.Name()+".p12"), tlsStore.Keystore.Password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("Failed tp import keys from %s into CMS Keystore: %v", filepath.Join(keystoreDir, keySet.Name()+".p12"), err)
|
return "", fmt.Errorf("Failed to import keys from %s into CMS Keystore: %v", filepath.Join(keystoreDir, keySet.Name()+".p12"), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Relabel the certificate in the CMS Keystore
|
// Relabel the certificate in the CMS Keystore
|
||||||
err = relabelCertificate(keySet.Name(), cmsKeystore)
|
err = relabelCertificate(keySet.Name(), &tlsStore.Keystore)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@@ -238,8 +295,8 @@ func processKeys(cmsKeystore, p12Truststore *KeyStoreData) (string, error) {
|
|||||||
return keyLabel, nil
|
return keyLabel, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// processTrustCertificates processes all trust certificates - adding them to the CMS KeyStore & PKCS#12 Truststore
|
// processTrustCertificates processes all trust certificates - adding them to the CMS KeyStore & PKCS#12 Truststore (if required)
|
||||||
func processTrustCertificates(cmsKeystore, p12Truststore *KeyStoreData) error {
|
func processTrustCertificates(tlsStore *TLSStore, trustDir string) error {
|
||||||
|
|
||||||
// Process all trust certiifcates
|
// Process all trust certiifcates
|
||||||
trustList, err := ioutil.ReadDir(trustDir)
|
trustList, err := ioutil.ReadDir(trustDir)
|
||||||
@@ -265,13 +322,14 @@ func processTrustCertificates(cmsKeystore, p12Truststore *KeyStoreData) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add to known certificates for the CMS Keystore
|
// Add to known certificates for the CMS Keystore
|
||||||
err = addToKnownCertificates(block, cmsKeystore, true)
|
err = addToKnownCertificates(block, &tlsStore.Keystore, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Failed to add to know certificates for CMS Keystore")
|
return fmt.Errorf("Failed to add to know certificates for CMS Keystore")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if tlsStore.Truststore.Keystore != nil {
|
||||||
// Add to known certificates for the PKCS#12 Truststore
|
// Add to known certificates for the PKCS#12 Truststore
|
||||||
err = addToKnownCertificates(block, p12Truststore, true)
|
err = addToKnownCertificates(block, &tlsStore.Truststore, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Failed to add to know certificates for PKCS#12 Truststore")
|
return fmt.Errorf("Failed to add to know certificates for PKCS#12 Truststore")
|
||||||
}
|
}
|
||||||
@@ -280,18 +338,19 @@ func processTrustCertificates(cmsKeystore, p12Truststore *KeyStoreData) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Add all trust certificates to PKCS#12 Truststore
|
// Add all trust certificates to PKCS#12 Truststore (if required)
|
||||||
if len(p12Truststore.TrustedCerts) > 0 {
|
if tlsStore.Truststore.Keystore != nil && len(tlsStore.Truststore.TrustedCerts) > 0 {
|
||||||
err = addCertificatesToTruststore(p12Truststore)
|
err = addCertificatesToTruststore(&tlsStore.Truststore)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add all trust certificates to CMS Keystore
|
// Add all trust certificates to CMS Keystore
|
||||||
if len(cmsKeystore.TrustedCerts) > 0 {
|
if len(tlsStore.Keystore.TrustedCerts) > 0 {
|
||||||
err = addCertificatesToCMSKeystore(cmsKeystore)
|
err = addCertificatesToCMSKeystore(&tlsStore.Keystore)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -301,7 +360,7 @@ func processTrustCertificates(cmsKeystore, p12Truststore *KeyStoreData) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// processPrivateKey processes the private key (*.key) from a set of keys
|
// processPrivateKey processes the private key (*.key) from a set of keys
|
||||||
func processPrivateKey(keySetName string, keys []os.FileInfo) (interface{}, string, error) {
|
func processPrivateKey(keyDir string, keySetName string, keys []os.FileInfo) (interface{}, string, error) {
|
||||||
|
|
||||||
var privateKey interface{}
|
var privateKey interface{}
|
||||||
keyPrefix := ""
|
keyPrefix := ""
|
||||||
@@ -336,7 +395,7 @@ func processPrivateKey(keySetName string, keys []os.FileInfo) (interface{}, stri
|
|||||||
}
|
}
|
||||||
|
|
||||||
// processCertificates processes the certificates (*.crt) from a set of keys
|
// processCertificates processes the certificates (*.crt) from a set of keys
|
||||||
func processCertificates(keySetName, keyPrefix string, keys []os.FileInfo, cmsKeystore, p12Truststore *KeyStoreData) (*x509.Certificate, []*x509.Certificate, error) {
|
func processCertificates(keyDir string, keySetName, keyPrefix string, keys []os.FileInfo, cmsKeystore, p12Truststore *KeyStoreData) (*x509.Certificate, []*x509.Certificate, error) {
|
||||||
|
|
||||||
var publicCertificate *x509.Certificate
|
var publicCertificate *x509.Certificate
|
||||||
var caCertificate []*x509.Certificate
|
var caCertificate []*x509.Certificate
|
||||||
@@ -384,11 +443,13 @@ func processCertificates(keySetName, keyPrefix string, keys []os.FileInfo, cmsKe
|
|||||||
return nil, nil, fmt.Errorf("Failed to add to know certificates for CMS Keystore")
|
return nil, nil, fmt.Errorf("Failed to add to know certificates for CMS Keystore")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if p12Truststore.Keystore != nil {
|
||||||
// Add to known certificates for the PKCS#12 Truststore
|
// Add to known certificates for the PKCS#12 Truststore
|
||||||
err = addToKnownCertificates(block, p12Truststore, true)
|
err = addToKnownCertificates(block, p12Truststore, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("Failed to add to know certificates for PKCS#12 Truststore")
|
return nil, nil, fmt.Errorf("Failed to add to know certificates for PKCS#12 Truststore")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
certificate, err := x509.ParseCertificate(block.Bytes)
|
certificate, err := x509.ParseCertificate(block.Bytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -510,6 +571,7 @@ func generateRandomPassword() string {
|
|||||||
validcharArray := []byte(validChars)
|
validcharArray := []byte(validChars)
|
||||||
password := ""
|
password := ""
|
||||||
for i := 0; i < 12; i++ {
|
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))])
|
password = password + string(validcharArray[pwr.Intn(len(validcharArray))])
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -554,10 +616,13 @@ func getCertificateFingerprint(block *pem.Block) (string, error) {
|
|||||||
|
|
||||||
// writeCertificatesToFile writes a list of certificates to a file
|
// writeCertificatesToFile writes a list of certificates to a file
|
||||||
func writeCertificatesToFile(file string, certificates []*pem.Block) error {
|
func writeCertificatesToFile(file string, certificates []*pem.Block) error {
|
||||||
|
|
||||||
|
// #nosec G304 - this is a temporary pem file to write certs.
|
||||||
f, err := os.Create(file)
|
f, err := os.Create(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Failed to create file %s: %v", file, err)
|
return fmt.Errorf("Failed to create file %s: %v", file, err)
|
||||||
}
|
}
|
||||||
|
// #nosec G307 - local to this function, pose no harm.
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
|
|
||||||
w := bufio.NewWriter(f)
|
w := bufio.NewWriter(f)
|
||||||
@@ -574,3 +639,39 @@ func writeCertificatesToFile(file string, certificates []*pem.Block) error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Search the specified directory for .key and .crt files.
|
||||||
|
// Return true if at least one .key or .crt file is found else false
|
||||||
|
func haveKeysAndCerts(keyDir string) bool {
|
||||||
|
fileList, err := os.ReadDir(keyDir)
|
||||||
|
if err == nil && len(fileList) > 0 {
|
||||||
|
for _, fileInfo := range fileList {
|
||||||
|
// Keys and certs will be supplied in an user defined subdirectory.
|
||||||
|
// 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.HasSuffix(key.Name(), ".key") || strings.HasSuffix(key.Name(), ".crt") {
|
||||||
|
// We found at least one key/crt file.
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
© Copyright IBM Corporation 2019, 2020
|
© Copyright IBM Corporation 2019, 2021
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -60,12 +60,11 @@ func ConfigureWebKeystore(p12Truststore KeyStoreData, webKeystore string) (strin
|
|||||||
if webKeystore == "" {
|
if webKeystore == "" {
|
||||||
webKeystore = webKeystoreDefault
|
webKeystore = webKeystoreDefault
|
||||||
}
|
}
|
||||||
webKeystoreFile := filepath.Join(keystoreDir, webKeystore)
|
webKeystoreFile := filepath.Join(keystoreDirDefault, webKeystore)
|
||||||
|
|
||||||
// Check if a new self-signed certificate should be generated
|
// Check if a new self-signed certificate should be generated
|
||||||
genHostName := os.Getenv("MQ_GENERATE_CERTIFICATE_HOSTNAME")
|
genHostName := os.Getenv("MQ_GENERATE_CERTIFICATE_HOSTNAME")
|
||||||
if genHostName != "" {
|
if genHostName != "" {
|
||||||
|
|
||||||
// Create the Web Keystore
|
// Create the Web Keystore
|
||||||
newWebKeystore := keystore.NewPKCS12KeyStore(webKeystoreFile, p12Truststore.Password)
|
newWebKeystore := keystore.NewPKCS12KeyStore(webKeystoreFile, p12Truststore.Password)
|
||||||
err := newWebKeystore.Create()
|
err := newWebKeystore.Create()
|
||||||
@@ -78,7 +77,6 @@ func ConfigureWebKeystore(p12Truststore KeyStoreData, webKeystore string) (strin
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("Failed to generate certificate in Web Keystore %s with DN of 'CN=%s': %v", webKeystoreFile, genHostName, err)
|
return "", fmt.Errorf("Failed to generate certificate in Web Keystore %s with DN of 'CN=%s': %v", webKeystoreFile, genHostName, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// Check Web Keystore already exists
|
// Check Web Keystore already exists
|
||||||
_, err := os.Stat(webKeystoreFile)
|
_, 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");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with 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
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
© Copyright IBM Corporation 2018, 2019
|
© Copyright IBM Corporation 2018, 2021
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -117,14 +117,22 @@ func (l *Logger) log(level string, msg string) {
|
|||||||
// Debug logs a line as debug
|
// Debug logs a line as debug
|
||||||
func (l *Logger) Debug(args ...interface{}) {
|
func (l *Logger) Debug(args ...interface{}) {
|
||||||
if l.debug {
|
if l.debug {
|
||||||
|
if l.json {
|
||||||
l.log(debugLevel, fmt.Sprint(args...))
|
l.log(debugLevel, fmt.Sprint(args...))
|
||||||
|
} else {
|
||||||
|
l.log(debugLevel, "DEBUG: "+fmt.Sprint(args...))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Debugf logs a line as debug using format specifiers
|
// Debugf logs a line as debug using format specifiers
|
||||||
func (l *Logger) Debugf(format string, args ...interface{}) {
|
func (l *Logger) Debugf(format string, args ...interface{}) {
|
||||||
if l.debug {
|
if l.debug {
|
||||||
|
if l.json {
|
||||||
l.log(debugLevel, fmt.Sprintf(format, args...))
|
l.log(debugLevel, fmt.Sprintf(format, args...))
|
||||||
|
} else {
|
||||||
|
l.log(debugLevel, fmt.Sprintf("DEBUG: "+format, args...))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
3
renovate.json
Normal file
3
renovate.json
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://docs.renovatebot.com/renovate-schema.json"
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# -*- mode: sh -*-
|
# -*- mode: sh -*-
|
||||||
# © Copyright IBM Corporation 2015, 2020
|
# © Copyright IBM Corporation 2015, 2023
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@@ -21,49 +21,8 @@ set -ex
|
|||||||
test -f /usr/bin/rpm && RPM=true || RPM=false
|
test -f /usr/bin/rpm && RPM=true || RPM=false
|
||||||
test -f /usr/bin/apt-get && UBUNTU=true || UBUNTU=false
|
test -f /usr/bin/apt-get && UBUNTU=true || UBUNTU=false
|
||||||
|
|
||||||
# Only install the SDK package as part of the build stage
|
|
||||||
INSTALL_SDK=${INSTALL_SDK:-0}
|
|
||||||
|
|
||||||
# Download and extract the MQ unzippable server
|
|
||||||
DIR_TMP=/tmp/mq
|
|
||||||
mkdir -p ${DIR_TMP}
|
|
||||||
cd ${DIR_TMP}
|
|
||||||
curl -LO $MQ_URL
|
|
||||||
|
|
||||||
tar -xzf ./*.tar.gz
|
|
||||||
rm -f ./*.tar.gz
|
|
||||||
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=$INSTALL_SDK
|
|
||||||
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
|
# Accept the MQ license
|
||||||
${INSTALLATION_DIR}/bin/mqlicense -accept
|
/opt/mqm/bin/mqlicense -accept
|
||||||
|
|
||||||
# Optional: Update the command prompt with the MQ version
|
# Optional: Update the command prompt with the MQ version
|
||||||
$UBUNTU && echo "mq:$(dspmqver -b -f 2)" > /etc/debian_chroot
|
$UBUNTU && echo "mq:$(dspmqver -b -f 2)" > /etc/debian_chroot
|
||||||
@@ -97,8 +56,8 @@ $RPM && PAM_FILE=/etc/pam.d/password-auth
|
|||||||
sed -i 's/password\t\[success=1 default=ignore\]\tpam_unix\.so obscure sha512/password\t[success=1 default=ignore]\tpam_unix.so obscure sha512 minlen=8/' $PAM_FILE
|
sed -i 's/password\t\[success=1 default=ignore\]\tpam_unix\.so obscure sha512/password\t[success=1 default=ignore]\tpam_unix.so obscure sha512 minlen=8/' $PAM_FILE
|
||||||
|
|
||||||
# List all the installed packages, for the build log
|
# List all the installed packages, for the build log
|
||||||
$RPM && rpm -q --all || true
|
$RPM && (rpm -q --all | sort) || true
|
||||||
$UBUNTU && dpkg --list || true
|
$UBUNTU && (dpkg --list | sort) || true
|
||||||
|
|
||||||
#Update the license file to include UBI 8 instead of UBI 7
|
#Update the license file to include UBI 8 instead of UBI 7
|
||||||
sed -i 's/v7.0/v8.0/g' /opt/mqm/licenses/non_ibm_license.txt
|
sed -i 's/v7.0/v8.0/g' /opt/mqm/licenses/non_ibm_license.txt
|
||||||
7
source-branch.env
Normal file
7
source-branch.env
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
###########################################################################################################################################################
|
||||||
|
|
||||||
|
# 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 ?= private-master
|
||||||
|
|
||||||
|
###########################################################################################################################################################
|
||||||
669
test/container/containerengine/containerengine.go
Normal file
669
test/container/containerengine/containerengine.go
Normal file
@@ -0,0 +1,669 @@
|
|||||||
|
/*
|
||||||
|
© 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.
|
||||||
|
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 containerengine
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ContainerInterface interface {
|
||||||
|
ContainerCreate(config *ContainerConfig, hostConfig *ContainerHostConfig, networkingConfig *ContainerNetworkSettings, containerName string) (string, error)
|
||||||
|
ContainerStop(container string, timeout *time.Duration) error
|
||||||
|
ContainerKill(container string, signal string) error
|
||||||
|
ContainerRemove(container string, options ContainerRemoveOptions) error
|
||||||
|
ContainerStart(container string, options ContainerStartOptions) error
|
||||||
|
ContainerWait(ctx context.Context, container string, condition string) (<-chan int64, <-chan error)
|
||||||
|
GetContainerLogs(ctx context.Context, container string, options ContainerLogsOptions) (string, error)
|
||||||
|
CopyFromContainer(container, srcPath string) ([]byte, error)
|
||||||
|
|
||||||
|
GetContainerPort(ID string, hostPort int) (string, error)
|
||||||
|
GetContainerIPAddress(ID string) (string, error)
|
||||||
|
ContainerInspectWithFormat(format string, ID string) (string, error)
|
||||||
|
ExecContainer(ID string, user string, cmd []string) (int, string)
|
||||||
|
GetMQVersion(image string) (string, error)
|
||||||
|
ContainerInspect(containerID string) (ContainerDetails, error)
|
||||||
|
|
||||||
|
NetworkCreate(name string, options NetworkCreateOptions) (string, error)
|
||||||
|
NetworkRemove(network string) error
|
||||||
|
|
||||||
|
VolumeCreate(options VolumeCreateOptions) (string, error)
|
||||||
|
VolumeRemove(volumeID string, force bool) error
|
||||||
|
|
||||||
|
ImageBuild(context io.Reader, tag string, dockerfilename string) (string, error)
|
||||||
|
ImageRemove(image string, options ImageRemoveOptions) (bool, error)
|
||||||
|
ImageInspectWithFormat(format string, ID string) (string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type ContainerClient struct {
|
||||||
|
ContainerTool string
|
||||||
|
Version string
|
||||||
|
}
|
||||||
|
|
||||||
|
// objects
|
||||||
|
var objVolume = "volume"
|
||||||
|
var objImage = "image"
|
||||||
|
var objPort = "port"
|
||||||
|
var objNetwork = "network"
|
||||||
|
|
||||||
|
// verbs
|
||||||
|
var listContainers = "ps"
|
||||||
|
var listImages = "images"
|
||||||
|
var create = "create"
|
||||||
|
var startContainer = "start"
|
||||||
|
var waitContainer = "wait"
|
||||||
|
var execContainer = "exec"
|
||||||
|
var getLogs = "logs"
|
||||||
|
var stopContainer = "stop"
|
||||||
|
var remove = "rm"
|
||||||
|
var inspect = "inspect"
|
||||||
|
var copyFile = "cp"
|
||||||
|
var build = "build"
|
||||||
|
var killContainer = "kill"
|
||||||
|
|
||||||
|
// args
|
||||||
|
var argEntrypoint = "--entrypoint"
|
||||||
|
var argUser = "--user"
|
||||||
|
var argExpose = "--expose"
|
||||||
|
var argVolume = "--volume"
|
||||||
|
var argPublish = "--publish"
|
||||||
|
var argPrivileged = "--privileged"
|
||||||
|
var argAddCapability = "--cap-add"
|
||||||
|
var argDropCapability = "--cap-drop"
|
||||||
|
var argName = "--name"
|
||||||
|
var argCondition = "--condition"
|
||||||
|
var argEnvironmentVariable = "--env"
|
||||||
|
var argTail = "--tail"
|
||||||
|
var argForce = "--force"
|
||||||
|
var argVolumes = "--volumes"
|
||||||
|
var argHostname = "--hostname"
|
||||||
|
var argDriver = "--driver"
|
||||||
|
var argFile = "--file"
|
||||||
|
var argQuiet = "--quiet"
|
||||||
|
var argTag = "--tag"
|
||||||
|
var argFormat = "--format"
|
||||||
|
var argNetwork = "--network"
|
||||||
|
var argSecurityOptions = "--security-opt"
|
||||||
|
var argSignal = "--signal"
|
||||||
|
|
||||||
|
// generic
|
||||||
|
var toolVersion = "version"
|
||||||
|
var ContainerStateNotRunning = "not-running"
|
||||||
|
var ContainerStateStopped = "stopped"
|
||||||
|
|
||||||
|
type ContainerConfig struct {
|
||||||
|
Image string
|
||||||
|
Hostname string
|
||||||
|
User string
|
||||||
|
Entrypoint []string
|
||||||
|
Env []string
|
||||||
|
ExposedPorts []string
|
||||||
|
}
|
||||||
|
|
||||||
|
type ContainerDetails struct {
|
||||||
|
ID string
|
||||||
|
Name string
|
||||||
|
Image string
|
||||||
|
Path string
|
||||||
|
Args []string
|
||||||
|
Config ContainerConfig
|
||||||
|
HostConfig ContainerHostConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
type ContainerDetailsLogging struct {
|
||||||
|
ID string
|
||||||
|
Name string
|
||||||
|
Image string
|
||||||
|
Path string
|
||||||
|
Args []string
|
||||||
|
CapAdd []string
|
||||||
|
CapDrop []string
|
||||||
|
User string
|
||||||
|
Env []string
|
||||||
|
}
|
||||||
|
|
||||||
|
type ContainerHostConfig struct {
|
||||||
|
Binds []string // Bindings onto a volume
|
||||||
|
PortBindings []PortBinding //Bindings from a container port to a port on the host
|
||||||
|
Privileged bool // Give extended privileges to container
|
||||||
|
CapAdd []string // Linux capabilities to add to the container
|
||||||
|
CapDrop []string // Linux capabilities to drop from the container
|
||||||
|
SecurityOpt []string
|
||||||
|
}
|
||||||
|
|
||||||
|
type ContainerNetworkSettings struct {
|
||||||
|
Networks []string // A list of networks to connect the container to
|
||||||
|
}
|
||||||
|
|
||||||
|
type ContainerRemoveOptions struct {
|
||||||
|
Force bool
|
||||||
|
RemoveVolumes bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type ContainerStartOptions struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
type NetworkCreateOptions struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
type ContainerLogsOptions struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
type ImageRemoveOptions struct {
|
||||||
|
Force bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type VolumeCreateOptions struct {
|
||||||
|
Name string
|
||||||
|
Driver string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Binding from a container port to a port on the host
|
||||||
|
type PortBinding struct {
|
||||||
|
HostIP string
|
||||||
|
HostPort string //Port to map to on the host
|
||||||
|
ContainerPort string //Exposed port on the container
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewContainerClient returns a new container client
|
||||||
|
// Defaults to using podman
|
||||||
|
func NewContainerClient() ContainerClient {
|
||||||
|
tool, set := os.LookupEnv("COMMAND")
|
||||||
|
if !set {
|
||||||
|
tool = "podman"
|
||||||
|
}
|
||||||
|
return ContainerClient{
|
||||||
|
ContainerTool: tool,
|
||||||
|
Version: GetContainerToolVersion(tool),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetContainerToolVersion returns the version of the container tool being used
|
||||||
|
func GetContainerToolVersion(containerTool string) string {
|
||||||
|
if containerTool == "docker" {
|
||||||
|
args := []string{"version", "--format", "'{{.Client.Version}}'"}
|
||||||
|
v, err := exec.Command("docker", args...).Output()
|
||||||
|
if err != nil {
|
||||||
|
return "0.0.0"
|
||||||
|
}
|
||||||
|
return string(v)
|
||||||
|
} else if containerTool == "podman" {
|
||||||
|
//Default to checking the version of podman
|
||||||
|
args := []string{"version", "--format", "'{{.Version}}'"}
|
||||||
|
v, err := exec.Command("podman", args...).Output()
|
||||||
|
if err != nil {
|
||||||
|
return "0.0.0"
|
||||||
|
}
|
||||||
|
return string(v)
|
||||||
|
}
|
||||||
|
return "0.0.0"
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMQVersion returns the MQ version of a given container image
|
||||||
|
func (cli ContainerClient) GetMQVersion(image string) (string, error) {
|
||||||
|
v, err := cli.ImageInspectWithFormat("{{.Config.Labels.version}}", image)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImageInspectWithFormat inspects an image with a given formatting string
|
||||||
|
func (cli ContainerClient) ImageInspectWithFormat(format string, ID string) (string, error) {
|
||||||
|
args := []string{
|
||||||
|
objImage,
|
||||||
|
inspect,
|
||||||
|
ID,
|
||||||
|
}
|
||||||
|
if format != "" {
|
||||||
|
args = append(args, []string{argFormat, format}...)
|
||||||
|
}
|
||||||
|
output, err := exec.Command(cli.ContainerTool, args...).Output()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return string(output), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainerInspectWithFormat inspects a container with a given formatting string
|
||||||
|
func (cli ContainerClient) ContainerInspectWithFormat(format string, ID string) (string, error) {
|
||||||
|
args := []string{
|
||||||
|
inspect,
|
||||||
|
ID,
|
||||||
|
}
|
||||||
|
if format != "" {
|
||||||
|
args = append(args, []string{argFormat, format}...)
|
||||||
|
}
|
||||||
|
output, err := exec.Command(cli.ContainerTool, args...).Output()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return string(output), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetContainerPort gets the ports on a container
|
||||||
|
func (cli ContainerClient) GetContainerPort(ID string, hostPort int) (string, error) {
|
||||||
|
args := []string{
|
||||||
|
objPort,
|
||||||
|
ID,
|
||||||
|
strconv.Itoa(hostPort),
|
||||||
|
}
|
||||||
|
output, err := exec.Command(cli.ContainerTool, args...).Output()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
o := SanitizeString(string(output))
|
||||||
|
return strings.Split((o), ":")[1], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetContainerIPAddress gets the IP address of a container
|
||||||
|
func (cli ContainerClient) GetContainerIPAddress(ID string) (string, error) {
|
||||||
|
v, err := cli.ContainerInspectWithFormat("{{.NetworkSettings.IPAddress}}", ID)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CopyFromContainer copies a file from a container and returns its contents
|
||||||
|
func (cli ContainerClient) CopyFromContainer(container, srcPath string) ([]byte, error) {
|
||||||
|
tmpDir, err := os.MkdirTemp("", "tmp")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(tmpDir)
|
||||||
|
args := []string{
|
||||||
|
copyFile,
|
||||||
|
container + ":" + srcPath,
|
||||||
|
tmpDir + "/.",
|
||||||
|
}
|
||||||
|
_, err = exec.Command(cli.ContainerTool, args...).CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
//Get file name
|
||||||
|
fname := filepath.Base(srcPath)
|
||||||
|
data, err := os.ReadFile(filepath.Join(tmpDir, fname))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
//Remove the file
|
||||||
|
err = os.Remove(filepath.Join(tmpDir, fname))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cli ContainerClient) ContainerInspect(containerID string) (ContainerDetails, error) {
|
||||||
|
args := []string{
|
||||||
|
inspect,
|
||||||
|
containerID,
|
||||||
|
}
|
||||||
|
output, err := exec.Command(cli.ContainerTool, args...).Output()
|
||||||
|
if err != nil {
|
||||||
|
return ContainerDetails{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var container ContainerDetails
|
||||||
|
err = json.Unmarshal(output, &container)
|
||||||
|
if err != nil {
|
||||||
|
return ContainerDetails{}, err
|
||||||
|
}
|
||||||
|
return container, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cli ContainerClient) ContainerStop(container string, timeout *time.Duration) error {
|
||||||
|
args := []string{
|
||||||
|
stopContainer,
|
||||||
|
container,
|
||||||
|
}
|
||||||
|
_, err := exec.Command(cli.ContainerTool, args...).Output()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cli ContainerClient) ContainerKill(container string, signal string) error {
|
||||||
|
args := []string{
|
||||||
|
killContainer,
|
||||||
|
container,
|
||||||
|
}
|
||||||
|
if signal != "" {
|
||||||
|
args = append(args, []string{argSignal, signal}...)
|
||||||
|
}
|
||||||
|
_, err := exec.Command(cli.ContainerTool, args...).Output()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cli ContainerClient) ContainerRemove(container string, options ContainerRemoveOptions) error {
|
||||||
|
args := []string{
|
||||||
|
remove,
|
||||||
|
container,
|
||||||
|
}
|
||||||
|
if options.Force {
|
||||||
|
args = append(args, argForce)
|
||||||
|
}
|
||||||
|
if options.RemoveVolumes {
|
||||||
|
args = append(args, argVolumes)
|
||||||
|
}
|
||||||
|
_, err := exec.Command(cli.ContainerTool, args...).Output()
|
||||||
|
if err != nil {
|
||||||
|
//Silently error as the exit code 125 is present on sucessful deletion
|
||||||
|
if strings.Contains(err.Error(), "125") {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cli ContainerClient) ExecContainer(ID string, user string, cmd []string) (int, string) {
|
||||||
|
args := []string{
|
||||||
|
execContainer,
|
||||||
|
}
|
||||||
|
if user != "" {
|
||||||
|
args = append(args, []string{argUser, user}...)
|
||||||
|
}
|
||||||
|
args = append(args, ID)
|
||||||
|
args = append(args, cmd...)
|
||||||
|
ctx := context.Background()
|
||||||
|
output, err := exec.CommandContext(ctx, cli.ContainerTool, args...).CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
if err.(*exec.ExitError) != nil {
|
||||||
|
return err.(*exec.ExitError).ExitCode(), string(output)
|
||||||
|
} else {
|
||||||
|
return 9897, string(output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0, string(output)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cli ContainerClient) ContainerStart(container string, options ContainerStartOptions) error {
|
||||||
|
args := []string{
|
||||||
|
startContainer,
|
||||||
|
container,
|
||||||
|
}
|
||||||
|
_, err := exec.Command(cli.ContainerTool, args...).Output()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainerWait starts waiting for a container. It returns an int64 channel for receiving an exit code and an error channel for receiving errors.
|
||||||
|
// The channels returned from this function should be used to receive the results from the wait command.
|
||||||
|
func (cli ContainerClient) ContainerWait(ctx context.Context, container string, condition string) (<-chan int64, <-chan error) {
|
||||||
|
args := []string{
|
||||||
|
waitContainer,
|
||||||
|
container,
|
||||||
|
}
|
||||||
|
if cli.ContainerTool == "podman" {
|
||||||
|
if condition == ContainerStateNotRunning {
|
||||||
|
condition = ContainerStateStopped
|
||||||
|
}
|
||||||
|
args = append(args, []string{argCondition, string(condition)}...)
|
||||||
|
}
|
||||||
|
|
||||||
|
resultC := make(chan int64)
|
||||||
|
errC := make(chan error, 1)
|
||||||
|
|
||||||
|
output, err := exec.CommandContext(ctx, cli.ContainerTool, args...).Output()
|
||||||
|
if err != nil {
|
||||||
|
errC <- err
|
||||||
|
return resultC, errC
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
out := strings.TrimSuffix(string(output), "\n")
|
||||||
|
exitCode, err := strconv.Atoi(out)
|
||||||
|
if err != nil {
|
||||||
|
errC <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resultC <- int64(exitCode)
|
||||||
|
}()
|
||||||
|
|
||||||
|
return resultC, errC
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cli ContainerClient) GetContainerLogs(ctx context.Context, container string, options ContainerLogsOptions) (string, error) {
|
||||||
|
args := []string{
|
||||||
|
getLogs,
|
||||||
|
container,
|
||||||
|
}
|
||||||
|
output, err := exec.Command(cli.ContainerTool, args...).CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return string(output), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cli ContainerClient) NetworkCreate(name string, options NetworkCreateOptions) (string, error) {
|
||||||
|
args := []string{
|
||||||
|
objNetwork,
|
||||||
|
create,
|
||||||
|
}
|
||||||
|
netID, err := exec.Command(cli.ContainerTool, args...).CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
networkID := SanitizeString(string(netID))
|
||||||
|
|
||||||
|
return networkID, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cli ContainerClient) NetworkRemove(network string) error {
|
||||||
|
args := []string{
|
||||||
|
objNetwork,
|
||||||
|
remove,
|
||||||
|
}
|
||||||
|
_, err := exec.Command(cli.ContainerTool, args...).CombinedOutput()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cli ContainerClient) VolumeCreate(options VolumeCreateOptions) (string, error) {
|
||||||
|
args := []string{
|
||||||
|
objVolume,
|
||||||
|
create,
|
||||||
|
options.Name,
|
||||||
|
}
|
||||||
|
if options.Driver != "" {
|
||||||
|
args = append(args, []string{argDriver, options.Driver}...)
|
||||||
|
}
|
||||||
|
output, err := exec.Command(cli.ContainerTool, args...).Output()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
name := SanitizeString(string(output))
|
||||||
|
return name, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cli ContainerClient) VolumeRemove(volumeID string, force bool) error {
|
||||||
|
args := []string{
|
||||||
|
objVolume,
|
||||||
|
remove,
|
||||||
|
volumeID,
|
||||||
|
}
|
||||||
|
if force {
|
||||||
|
args = append(args, argForce)
|
||||||
|
}
|
||||||
|
_, err := exec.Command(cli.ContainerTool, args...).Output()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cli ContainerClient) ImageBuild(context io.Reader, tag string, dockerfilename string) (string, error) {
|
||||||
|
args := []string{
|
||||||
|
objImage,
|
||||||
|
build,
|
||||||
|
}
|
||||||
|
//dockerfilename includes the path to the dockerfile
|
||||||
|
//When using podman use the full path including the name of the Dockerfile
|
||||||
|
if cli.ContainerTool == "podman" {
|
||||||
|
args = append(args, []string{argFile, dockerfilename}...)
|
||||||
|
}
|
||||||
|
if tag != "" {
|
||||||
|
args = append(args, []string{argTag, tag}...)
|
||||||
|
}
|
||||||
|
args = append(args, argQuiet)
|
||||||
|
//When using docker remove the name 'DockerFile' from the string
|
||||||
|
if cli.ContainerTool == "docker" {
|
||||||
|
dfn := strings.ReplaceAll(dockerfilename, "Dockerfile", "")
|
||||||
|
args = append(args, dfn)
|
||||||
|
}
|
||||||
|
output, err := exec.Command(cli.ContainerTool, args...).Output()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
sha := SanitizeString(string(output))
|
||||||
|
return sha, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cli ContainerClient) ImageRemove(image string, options ImageRemoveOptions) (bool, error) {
|
||||||
|
args := []string{
|
||||||
|
objImage,
|
||||||
|
remove,
|
||||||
|
image,
|
||||||
|
}
|
||||||
|
if options.Force {
|
||||||
|
args = append(args, argForce)
|
||||||
|
}
|
||||||
|
_, err := exec.Command(cli.ContainerTool, args...).Output()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cli ContainerClient) ContainerCreate(config *ContainerConfig, hostConfig *ContainerHostConfig, networkingConfig *ContainerNetworkSettings, containerName string) (string, error) {
|
||||||
|
args := []string{
|
||||||
|
create,
|
||||||
|
argName,
|
||||||
|
containerName,
|
||||||
|
}
|
||||||
|
args = getHostConfigArgs(args, hostConfig)
|
||||||
|
args = getNetworkConfigArgs(args, networkingConfig)
|
||||||
|
args = getContainerConfigArgs(args, config, cli.ContainerTool)
|
||||||
|
output, err := exec.Command(cli.ContainerTool, args...).Output()
|
||||||
|
lines := strings.Split(strings.ReplaceAll(string(output), "\r\n", "\n"), "\n")
|
||||||
|
if err != nil {
|
||||||
|
return lines[0], err
|
||||||
|
}
|
||||||
|
return lines[0], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getContainerConfigArgs converts a ContainerConfig into a set of cli arguments
|
||||||
|
func getContainerConfigArgs(args []string, config *ContainerConfig, toolName string) []string {
|
||||||
|
argList := []string{}
|
||||||
|
if config.Entrypoint != nil && toolName == "podman" {
|
||||||
|
entrypoint := "[\""
|
||||||
|
for i, commandPart := range config.Entrypoint {
|
||||||
|
if i != len(config.Entrypoint)-1 {
|
||||||
|
entrypoint += commandPart + "\",\""
|
||||||
|
} else {
|
||||||
|
//terminate list
|
||||||
|
entrypoint += commandPart + "\"]"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
args = append(args, []string{argEntrypoint, entrypoint}...)
|
||||||
|
}
|
||||||
|
if config.Entrypoint != nil && toolName == "docker" {
|
||||||
|
ep1 := ""
|
||||||
|
for i, commandPart := range config.Entrypoint {
|
||||||
|
if i == 0 {
|
||||||
|
ep1 = commandPart
|
||||||
|
} else {
|
||||||
|
argList = append(argList, commandPart)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
args = append(args, []string{argEntrypoint, ep1}...)
|
||||||
|
}
|
||||||
|
if config.User != "" {
|
||||||
|
args = append(args, []string{argUser, config.User}...)
|
||||||
|
}
|
||||||
|
if config.ExposedPorts != nil {
|
||||||
|
for _, port := range config.ExposedPorts {
|
||||||
|
args = append(args, []string{argExpose, port}...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if config.Hostname != "" {
|
||||||
|
args = append(args, []string{argHostname, config.Hostname}...)
|
||||||
|
}
|
||||||
|
for _, env := range config.Env {
|
||||||
|
args = append(args, []string{argEnvironmentVariable, env}...)
|
||||||
|
}
|
||||||
|
if config.Image != "" {
|
||||||
|
args = append(args, config.Image)
|
||||||
|
}
|
||||||
|
if config.Entrypoint != nil && toolName == "docker" {
|
||||||
|
args = append(args, argList...)
|
||||||
|
}
|
||||||
|
return args
|
||||||
|
}
|
||||||
|
|
||||||
|
// getHostConfigArgs converts a ContainerHostConfig into a set of cli arguments
|
||||||
|
func getHostConfigArgs(args []string, hostConfig *ContainerHostConfig) []string {
|
||||||
|
if hostConfig.Binds != nil {
|
||||||
|
for _, volume := range hostConfig.Binds {
|
||||||
|
args = append(args, []string{argVolume, volume}...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if hostConfig.PortBindings != nil {
|
||||||
|
for _, binding := range hostConfig.PortBindings {
|
||||||
|
pub := binding.HostIP + ":" + binding.HostPort + ":" + binding.ContainerPort
|
||||||
|
args = append(args, []string{argPublish, pub}...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if hostConfig.Privileged {
|
||||||
|
args = append(args, []string{argPrivileged}...)
|
||||||
|
}
|
||||||
|
if hostConfig.CapAdd != nil {
|
||||||
|
for _, capability := range hostConfig.CapAdd {
|
||||||
|
args = append(args, []string{argAddCapability, string(capability)}...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if hostConfig.CapDrop != nil {
|
||||||
|
for _, capability := range hostConfig.CapDrop {
|
||||||
|
args = append(args, []string{argDropCapability, string(capability)}...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if hostConfig.SecurityOpt != nil {
|
||||||
|
for _, securityOption := range hostConfig.SecurityOpt {
|
||||||
|
args = append(args, []string{argSecurityOptions, string(securityOption)}...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return args
|
||||||
|
}
|
||||||
|
|
||||||
|
// getNetworkConfigArgs converts a set of ContainerNetworkSettings into a set of cli arguments
|
||||||
|
func getNetworkConfigArgs(args []string, networkingConfig *ContainerNetworkSettings) []string {
|
||||||
|
if networkingConfig.Networks != nil {
|
||||||
|
for _, netID := range networkingConfig.Networks {
|
||||||
|
args = append(args, []string{argNetwork, netID}...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return args
|
||||||
|
}
|
||||||
|
|
||||||
|
func SanitizeString(s string) string {
|
||||||
|
s = strings.Replace(s, " ", "", -1)
|
||||||
|
s = strings.Replace(s, "\t", "", -1)
|
||||||
|
s = strings.Replace(s, "\n", "", -1)
|
||||||
|
return s
|
||||||
|
}
|
||||||
760
test/container/devconfig_test.go
Normal file
760
test/container/devconfig_test.go
Normal file
@@ -0,0 +1,760 @@
|
|||||||
|
//go:build mqdev
|
||||||
|
// +build mqdev
|
||||||
|
|
||||||
|
/*
|
||||||
|
© 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.
|
||||||
|
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 (
|
||||||
|
"crypto/tls"
|
||||||
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
ce "github.com/ibm-messaging/mq-container/test/container/containerengine"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestDevGoldenPath tests using the default values for the default developer config.
|
||||||
|
// Note: This test requires a separate container image to be available for the JMS tests.
|
||||||
|
func TestDevGoldenPath(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
cli := ce.NewContainerClient()
|
||||||
|
qm := "qm1"
|
||||||
|
containerConfig := ce.ContainerConfig{
|
||||||
|
Env: []string{
|
||||||
|
"LICENSE=accept",
|
||||||
|
"MQ_QMGR_NAME=" + qm,
|
||||||
|
"DEBUG=true",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
id := runContainerWithPorts(t, cli, &containerConfig, []int{9443, 1414})
|
||||||
|
defer cleanContainer(t, cli, id)
|
||||||
|
waitForReady(t, cli, id)
|
||||||
|
waitForWebReady(t, cli, id, insecureTLSConfig)
|
||||||
|
t.Run("JMS", func(t *testing.T) {
|
||||||
|
// Run the JMS tests, with no password specified.
|
||||||
|
// Use OpenJDK JRE for running testing, pass false for 7th parameter.
|
||||||
|
// Last parameter is blank as the test doesn't use TLS.
|
||||||
|
runJMSTests(t, cli, id, false, "app", defaultAppPasswordOS, "false", "")
|
||||||
|
})
|
||||||
|
t.Run("REST admin", func(t *testing.T) {
|
||||||
|
testRESTAdmin(t, cli, id, insecureTLSConfig, "")
|
||||||
|
})
|
||||||
|
t.Run("REST messaging", func(t *testing.T) {
|
||||||
|
testRESTMessaging(t, cli, id, insecureTLSConfig, qm, "app", defaultAppPasswordWeb, "")
|
||||||
|
})
|
||||||
|
// Stop the container cleanly
|
||||||
|
stopContainer(t, cli, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestDevSecure tests the default developer config using the a custom TLS key store and password.
|
||||||
|
// Note: This test requires a separate container image to be available for the JMS tests
|
||||||
|
func TestDevSecure(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
cli := ce.NewContainerClient()
|
||||||
|
|
||||||
|
const tlsPassPhrase string = "passw0rd"
|
||||||
|
qm := "qm1"
|
||||||
|
appPassword := "differentPassw0rd"
|
||||||
|
containerConfig := ce.ContainerConfig{
|
||||||
|
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",
|
||||||
|
},
|
||||||
|
Image: imageName(),
|
||||||
|
}
|
||||||
|
hostConfig := ce.ContainerHostConfig{
|
||||||
|
Binds: []string{
|
||||||
|
coverageBind(t),
|
||||||
|
tlsDir(t, false) + ":/etc/mqm/pki/keys/default",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
// Assign a random port for the web server on the host
|
||||||
|
// TODO: Don't do this for all tests
|
||||||
|
var binding ce.PortBinding
|
||||||
|
ports := []int{9443, 1414}
|
||||||
|
for _, p := range ports {
|
||||||
|
port := fmt.Sprintf("%v/tcp", p)
|
||||||
|
binding = ce.PortBinding{
|
||||||
|
ContainerPort: port,
|
||||||
|
HostIP: "0.0.0.0",
|
||||||
|
}
|
||||||
|
hostConfig.PortBindings = append(hostConfig.PortBindings, binding)
|
||||||
|
}
|
||||||
|
networkingConfig := ce.ContainerNetworkSettings{}
|
||||||
|
ID, err := cli.ContainerCreate(&containerConfig, &hostConfig, &networkingConfig, t.Name())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer cleanContainer(t, cli, ID)
|
||||||
|
startContainer(t, cli, ID)
|
||||||
|
waitForReady(t, cli, ID)
|
||||||
|
cert := filepath.Join(tlsDir(t, true), "server.crt")
|
||||||
|
waitForWebReady(t, cli, ID, createTLSConfig(t, cert, tlsPassPhrase))
|
||||||
|
|
||||||
|
t.Run("JMS", func(t *testing.T) {
|
||||||
|
// OpenJDK is used for running tests, hence pass "false" for 7th parameter.
|
||||||
|
// Cipher name specified is compliant with non-IBM JRE naming.
|
||||||
|
runJMSTests(t, cli, ID, true, "app", appPassword, "false", "TLS_RSA_WITH_AES_256_CBC_SHA256")
|
||||||
|
})
|
||||||
|
t.Run("REST admin", func(t *testing.T) {
|
||||||
|
testRESTAdmin(t, cli, ID, insecureTLSConfig, "")
|
||||||
|
})
|
||||||
|
t.Run("REST messaging", func(t *testing.T) {
|
||||||
|
testRESTMessaging(t, cli, ID, insecureTLSConfig, qm, "app", appPassword, "")
|
||||||
|
})
|
||||||
|
|
||||||
|
// Stop the container cleanly
|
||||||
|
stopContainer(t, cli, ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDevWebDisabled(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
cli := ce.NewContainerClient()
|
||||||
|
containerConfig := ce.ContainerConfig{
|
||||||
|
Env: []string{
|
||||||
|
"LICENSE=accept",
|
||||||
|
"MQ_QMGR_NAME=qm1",
|
||||||
|
"MQ_ENABLE_EMBEDDED_WEB_SERVER=false",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
id := runContainerWithPorts(t, cli, &containerConfig, []int{1414})
|
||||||
|
defer cleanContainer(t, cli, id)
|
||||||
|
waitForReady(t, cli, id)
|
||||||
|
t.Run("Web", func(t *testing.T) {
|
||||||
|
_, dspmqweb := cli.ExecContainer(id, "", []string{"dspmqweb"})
|
||||||
|
if !strings.Contains(dspmqweb, "Server mqweb is not running.") && !strings.Contains(dspmqweb, "MQWB1125I") {
|
||||||
|
t.Errorf("Expected dspmqweb to say 'Server is not running' or 'MQWB1125I'; got \"%v\"", dspmqweb)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
t.Run("JMS", func(t *testing.T) {
|
||||||
|
// Run the JMS tests, with no password specified
|
||||||
|
// OpenJDK is used for running tests, hence pass "false" for 7th parameter.
|
||||||
|
// Last parameter is blank as the test doesn't use TLS.
|
||||||
|
runJMSTests(t, cli, id, false, "app", defaultAppPasswordOS, "false", "")
|
||||||
|
})
|
||||||
|
// Stop the container cleanly
|
||||||
|
stopContainer(t, cli, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDevConfigDisabled(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
cli := ce.NewContainerClient()
|
||||||
|
containerConfig := ce.ContainerConfig{
|
||||||
|
Env: []string{
|
||||||
|
"LICENSE=accept",
|
||||||
|
"MQ_QMGR_NAME=qm1",
|
||||||
|
"MQ_DEV=false",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
id := runContainerWithPorts(t, cli, &containerConfig, []int{9443})
|
||||||
|
defer cleanContainer(t, cli, id)
|
||||||
|
waitForReady(t, cli, id)
|
||||||
|
waitForWebReady(t, cli, id, insecureTLSConfig)
|
||||||
|
rc, _ := execContainer(t, cli, id, "", []string{"bash", "-c", "echo 'display qlocal(DEV*)' | runmqsc"})
|
||||||
|
if rc == 0 {
|
||||||
|
t.Errorf("Expected DEV queues to be missing")
|
||||||
|
}
|
||||||
|
// Stop the container cleanly
|
||||||
|
stopContainer(t, cli, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test if SSLKEYR and CERTLABL attributes are not set when key and certificate
|
||||||
|
// are not supplied.
|
||||||
|
func TestSSLKEYRBlank(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
cli := ce.NewContainerClient()
|
||||||
|
containerConfig := ce.ContainerConfig{
|
||||||
|
Env: []string{
|
||||||
|
"LICENSE=accept",
|
||||||
|
"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( )") {
|
||||||
|
// 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
|
||||||
|
stopContainer(t, cli, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test if SSLKEYR and CERTLABL attributes are set when key and certificate
|
||||||
|
// are supplied.
|
||||||
|
func TestSSLKEYRWithSuppliedKeyAndCert(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
cli := ce.NewContainerClient()
|
||||||
|
|
||||||
|
containerConfig := ce.ContainerConfig{
|
||||||
|
Env: []string{
|
||||||
|
"LICENSE=accept",
|
||||||
|
"MQ_QMGR_NAME=QM1",
|
||||||
|
"MQ_ENABLE_EMBEDDED_WEB_SERVER=false",
|
||||||
|
},
|
||||||
|
Image: imageName(),
|
||||||
|
}
|
||||||
|
hostConfig := ce.ContainerHostConfig{
|
||||||
|
Binds: []string{
|
||||||
|
coverageBind(t),
|
||||||
|
tlsDir(t, false) + ":/etc/mqm/pki/keys/default",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
networkingConfig := ce.ContainerNetworkSettings{}
|
||||||
|
ID, err := cli.ContainerCreate(&containerConfig, &hostConfig, &networkingConfig, t.Name())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer cleanContainer(t, cli, ID)
|
||||||
|
startContainer(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(/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, 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, 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 '/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
|
||||||
|
stopContainer(t, cli, ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test with CA cert
|
||||||
|
func TestSSLKEYRWithCACert(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
cli := ce.NewContainerClient()
|
||||||
|
|
||||||
|
containerConfig := ce.ContainerConfig{
|
||||||
|
Env: []string{
|
||||||
|
"LICENSE=accept",
|
||||||
|
"MQ_QMGR_NAME=QM1",
|
||||||
|
"MQ_ENABLE_EMBEDDED_WEB_SERVER=false",
|
||||||
|
},
|
||||||
|
Image: imageName(),
|
||||||
|
}
|
||||||
|
hostConfig := ce.ContainerHostConfig{
|
||||||
|
Binds: []string{
|
||||||
|
coverageBind(t),
|
||||||
|
tlsDirWithCA(t, false) + ":/etc/mqm/pki/keys/QM1CA",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
// Assign a random port for the web server on the host
|
||||||
|
var binding ce.PortBinding
|
||||||
|
ports := []int{9443}
|
||||||
|
for _, p := range ports {
|
||||||
|
port := fmt.Sprintf("%v/tcp", p)
|
||||||
|
binding = ce.PortBinding{
|
||||||
|
ContainerPort: port,
|
||||||
|
HostIP: "0.0.0.0",
|
||||||
|
}
|
||||||
|
hostConfig.PortBindings = append(hostConfig.PortBindings, binding)
|
||||||
|
}
|
||||||
|
networkingConfig := ce.ContainerNetworkSettings{}
|
||||||
|
ID, err := cli.ContainerCreate(&containerConfig, &hostConfig, &networkingConfig, t.Name())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer cleanContainer(t, cli, ID)
|
||||||
|
startContainer(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(/run/runmqserver/tls/key)") {
|
||||||
|
// 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(/run/runmqserver/tls/key)") {
|
||||||
|
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 '/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)") {
|
||||||
|
_, autoMQSC := execContainer(t, cli, 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, ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verifies SSLFIPS is set to NO if MQ_ENABLE_FIPS=false
|
||||||
|
func TestSSLFIPSNO(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
cli := ce.NewContainerClient()
|
||||||
|
|
||||||
|
containerConfig := ce.ContainerConfig{
|
||||||
|
Env: []string{
|
||||||
|
"LICENSE=accept",
|
||||||
|
"MQ_QMGR_NAME=QM1",
|
||||||
|
"MQ_ENABLE_EMBEDDED_WEB_SERVER=false",
|
||||||
|
"MQ_ENABLE_FIPS=false",
|
||||||
|
},
|
||||||
|
Image: imageName(),
|
||||||
|
}
|
||||||
|
hostConfig := ce.ContainerHostConfig{
|
||||||
|
Binds: []string{
|
||||||
|
coverageBind(t),
|
||||||
|
tlsDir(t, false) + ":/etc/mqm/pki/keys/default",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
networkingConfig := ce.ContainerNetworkSettings{}
|
||||||
|
ID, err := cli.ContainerCreate(&containerConfig, &hostConfig, &networkingConfig, t.Name())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer cleanContainer(t, cli, ID)
|
||||||
|
startContainer(t, cli, ID)
|
||||||
|
waitForReady(t, cli, ID)
|
||||||
|
|
||||||
|
// execute runmqsc to display qmgr SSLKEYR, SSLFIPS and CERTLABL attibutes.
|
||||||
|
// Search the console output for exepcted values
|
||||||
|
_, sslFIPSOutput := execContainer(t, cli, 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, 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 := ce.NewContainerClient()
|
||||||
|
|
||||||
|
appPassword := "differentPassw0rd"
|
||||||
|
containerConfig := ce.ContainerConfig{
|
||||||
|
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 := ce.ContainerHostConfig{
|
||||||
|
Binds: []string{
|
||||||
|
coverageBind(t),
|
||||||
|
tlsDir(t, false) + ":/etc/mqm/pki/keys/default",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
var binding ce.PortBinding
|
||||||
|
ports := []int{1414}
|
||||||
|
for _, p := range ports {
|
||||||
|
port := fmt.Sprintf("%v/tcp", p)
|
||||||
|
binding = ce.PortBinding{
|
||||||
|
ContainerPort: port,
|
||||||
|
HostIP: "0.0.0.0",
|
||||||
|
}
|
||||||
|
hostConfig.PortBindings = append(hostConfig.PortBindings, binding)
|
||||||
|
}
|
||||||
|
networkingConfig := ce.ContainerNetworkSettings{}
|
||||||
|
ID, err := cli.ContainerCreate(&containerConfig, &hostConfig, &networkingConfig, t.Name())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer cleanContainer(t, cli, ID)
|
||||||
|
startContainer(t, cli, ID)
|
||||||
|
waitForReady(t, cli, ID)
|
||||||
|
|
||||||
|
// Check for expected message on container log
|
||||||
|
logs := inspectLogs(t, cli, 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, 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, ID, true, "app", appPassword, "false", "TLS_RSA_WITH_AES_256_CBC_SHA256")
|
||||||
|
})
|
||||||
|
|
||||||
|
// Stop the container cleanly
|
||||||
|
stopContainer(t, cli, ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestDevSecureFIPSYESWeb verifies if the MQ Web Server is running in FIPS mode
|
||||||
|
func TestDevSecureFIPSTrueWeb(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
cli := ce.NewContainerClient()
|
||||||
|
|
||||||
|
const tlsPassPhrase string = "passw0rd"
|
||||||
|
qm := "qm1"
|
||||||
|
appPassword := "differentPassw0rd"
|
||||||
|
containerConfig := ce.ContainerConfig{
|
||||||
|
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 := ce.ContainerHostConfig{
|
||||||
|
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
|
||||||
|
var binding ce.PortBinding
|
||||||
|
ports := []int{9443}
|
||||||
|
for _, p := range ports {
|
||||||
|
port := fmt.Sprintf("%v/tcp", p)
|
||||||
|
binding = ce.PortBinding{
|
||||||
|
ContainerPort: port,
|
||||||
|
HostIP: "0.0.0.0",
|
||||||
|
}
|
||||||
|
hostConfig.PortBindings = append(hostConfig.PortBindings, binding)
|
||||||
|
}
|
||||||
|
networkingConfig := ce.ContainerNetworkSettings{}
|
||||||
|
ID, err := cli.ContainerCreate(&containerConfig, &hostConfig, &networkingConfig, t.Name())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer cleanContainer(t, cli, ID)
|
||||||
|
|
||||||
|
startContainer(t, cli, ID)
|
||||||
|
waitForReady(t, cli, ID)
|
||||||
|
cert := filepath.Join(tlsDir(t, true), "server.crt")
|
||||||
|
waitForWebReady(t, cli, 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, 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, ID, secureNonFIPSCipherConfig, qm, "app", appPassword, "EOF")
|
||||||
|
})
|
||||||
|
|
||||||
|
// Stop the container cleanly
|
||||||
|
stopContainer(t, cli, ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestDevSecureNOFIPSWeb verifies if the MQ Web Server is not running in FIPS mode
|
||||||
|
func TestDevSecureFalseFIPSWeb(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
cli := ce.NewContainerClient()
|
||||||
|
|
||||||
|
const tlsPassPhrase string = "passw0rd"
|
||||||
|
qm := "qm1"
|
||||||
|
appPassword := "differentPassw0rd"
|
||||||
|
containerConfig := ce.ContainerConfig{
|
||||||
|
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 := ce.ContainerHostConfig{
|
||||||
|
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
|
||||||
|
var binding ce.PortBinding
|
||||||
|
ports := []int{9443}
|
||||||
|
for _, p := range ports {
|
||||||
|
port := fmt.Sprintf("%v/tcp", p)
|
||||||
|
binding = ce.PortBinding{
|
||||||
|
ContainerPort: port,
|
||||||
|
HostIP: "0.0.0.0",
|
||||||
|
}
|
||||||
|
hostConfig.PortBindings = append(hostConfig.PortBindings, binding)
|
||||||
|
}
|
||||||
|
networkingConfig := ce.ContainerNetworkSettings{}
|
||||||
|
ID, err := cli.ContainerCreate(&containerConfig, &hostConfig, &networkingConfig, t.Name())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer cleanContainer(t, cli, ID)
|
||||||
|
startContainer(t, cli, ID)
|
||||||
|
waitForReady(t, cli, ID)
|
||||||
|
|
||||||
|
cert := filepath.Join(tlsDir(t, true), "server.crt")
|
||||||
|
waitForWebReady(t, cli, 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, 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, ID, secureTLSConfig, "")
|
||||||
|
})
|
||||||
|
|
||||||
|
// Stop the container cleanly
|
||||||
|
stopContainer(t, cli, ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify SSLFIPS is set to NO if no certificates were supplied
|
||||||
|
func TestSSLFIPSTrueNoCerts(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
cli := ce.NewContainerClient()
|
||||||
|
|
||||||
|
appPassword := "differentPassw0rd"
|
||||||
|
containerConfig := ce.ContainerConfig{
|
||||||
|
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 := ce.ContainerHostConfig{
|
||||||
|
Binds: []string{
|
||||||
|
coverageBind(t),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
networkingConfig := ce.ContainerNetworkSettings{}
|
||||||
|
ID, err := cli.ContainerCreate(&containerConfig, &hostConfig, &networkingConfig, t.Name())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer cleanContainer(t, cli, ID)
|
||||||
|
startContainer(t, cli, ID)
|
||||||
|
waitForReady(t, cli, ID)
|
||||||
|
|
||||||
|
// execute runmqsc to display qmgr SSLKEYR, SSLFIPS and CERTLABL attibutes.
|
||||||
|
// Search the console output for exepcted values
|
||||||
|
_, sslFIPSOutput := execContainer(t, cli, 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, ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verifies SSLFIPS is set to NO if MQ_ENABLE_FIPS=tru (invalid value)
|
||||||
|
func TestSSLFIPSInvalidValue(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
cli := ce.NewContainerClient()
|
||||||
|
|
||||||
|
containerConfig := ce.ContainerConfig{
|
||||||
|
Env: []string{
|
||||||
|
"LICENSE=accept",
|
||||||
|
"MQ_QMGR_NAME=QM1",
|
||||||
|
"MQ_ENABLE_EMBEDDED_WEB_SERVER=false",
|
||||||
|
"MQ_ENABLE_FIPS=tru",
|
||||||
|
},
|
||||||
|
Image: imageName(),
|
||||||
|
}
|
||||||
|
hostConfig := ce.ContainerHostConfig{
|
||||||
|
Binds: []string{
|
||||||
|
coverageBind(t),
|
||||||
|
tlsDir(t, false) + ":/etc/mqm/pki/keys/default",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
networkingConfig := ce.ContainerNetworkSettings{}
|
||||||
|
ID, err := cli.ContainerCreate(&containerConfig, &hostConfig, &networkingConfig, t.Name())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer cleanContainer(t, cli, ID)
|
||||||
|
startContainer(t, cli, ID)
|
||||||
|
waitForReady(t, cli, ID)
|
||||||
|
|
||||||
|
// execute runmqsc to display qmgr SSLKEYR, SSLFIPS and CERTLABL attibutes.
|
||||||
|
// Search the console output for exepcted values
|
||||||
|
_, sslFIPSOutput := execContainer(t, cli, 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, ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Container creation fails when invalid certs are passed and MQ_ENABLE_FIPS set true
|
||||||
|
func TestSSLFIPSBadCerts(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
cli := ce.NewContainerClient()
|
||||||
|
|
||||||
|
containerConfig := ce.ContainerConfig{
|
||||||
|
Env: []string{
|
||||||
|
"LICENSE=accept",
|
||||||
|
"MQ_QMGR_NAME=QM1",
|
||||||
|
"MQ_ENABLE_EMBEDDED_WEB_SERVER=false",
|
||||||
|
"MQ_ENABLE_FIPS=true",
|
||||||
|
},
|
||||||
|
Image: imageName(),
|
||||||
|
}
|
||||||
|
hostConfig := ce.ContainerHostConfig{
|
||||||
|
Binds: []string{
|
||||||
|
coverageBind(t),
|
||||||
|
tlsDirInvalid(t, false) + ":/etc/mqm/pki/keys/default",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
networkingConfig := ce.ContainerNetworkSettings{}
|
||||||
|
ID, err := cli.ContainerCreate(&containerConfig, &hostConfig, &networkingConfig, t.Name())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer cleanContainer(t, cli, ID)
|
||||||
|
startContainer(t, cli, ID)
|
||||||
|
|
||||||
|
rc := waitForContainer(t, cli, 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, 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
|
||||||
|
stopContainer(t, cli, ID)
|
||||||
|
}
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
|
//go:build mqdev
|
||||||
// +build mqdev
|
// +build mqdev
|
||||||
|
|
||||||
/*
|
/*
|
||||||
© Copyright IBM Corporation 2018, 2019
|
© Copyright IBM Corporation 2018, 2023
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -18,6 +19,7 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
@@ -26,15 +28,13 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httputil"
|
"net/http/httputil"
|
||||||
"os"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/docker/docker/api/types/container"
|
ce "github.com/ibm-messaging/mq-container/test/container/containerengine"
|
||||||
"github.com/docker/docker/api/types/network"
|
|
||||||
"github.com/docker/docker/client"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const defaultAdminPassword string = "passw0rd"
|
const defaultAdminPassword string = "passw0rd"
|
||||||
@@ -47,14 +47,19 @@ var insecureTLSConfig *tls.Config = &tls.Config{
|
|||||||
InsecureSkipVerify: true,
|
InsecureSkipVerify: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
func waitForWebReady(t *testing.T, cli *client.Client, ID string, tlsConfig *tls.Config) {
|
func waitForWebReady(t *testing.T, cli ce.ContainerInterface, ID string, tlsConfig *tls.Config) {
|
||||||
|
t.Logf("%s Waiting for web server to be ready", time.Now().Format(time.RFC3339))
|
||||||
httpClient := http.Client{
|
httpClient := http.Client{
|
||||||
Timeout: time.Duration(3 * time.Second),
|
Timeout: time.Duration(10 * time.Second),
|
||||||
Transport: &http.Transport{
|
Transport: &http.Transport{
|
||||||
TLSClientConfig: tlsConfig,
|
TLSClientConfig: tlsConfig,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
url := fmt.Sprintf("https://localhost:%s/ibmmq/rest/v1/admin/installation", getPort(t, cli, ID, 9443))
|
port, err := cli.GetContainerPort(ID, 9443)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
url := fmt.Sprintf("https://localhost:%s/ibmmq/rest/v1/admin/installation", port)
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
|
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
@@ -63,13 +68,13 @@ func waitForWebReady(t *testing.T, cli *client.Client, ID string, tlsConfig *tls
|
|||||||
case <-time.After(1 * time.Second):
|
case <-time.After(1 * time.Second):
|
||||||
req, err := http.NewRequest("GET", url, nil)
|
req, err := http.NewRequest("GET", url, nil)
|
||||||
req.SetBasicAuth("admin", defaultAdminPassword)
|
req.SetBasicAuth("admin", defaultAdminPassword)
|
||||||
resp, err := httpClient.Do(req.WithContext(ctx))
|
resp, err := httpClient.Do(req)
|
||||||
if err == nil && resp.StatusCode == http.StatusOK {
|
if err == nil && resp.StatusCode == http.StatusOK {
|
||||||
t.Log("MQ web server is ready")
|
t.Logf("%s MQ web server is ready", time.Now().Format(time.RFC3339))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
t.Fatal("Timed out waiting for web server to become ready")
|
t.Fatalf("%s Timed out waiting for web server to become ready", time.Now().Format(time.RFC3339))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -79,15 +84,28 @@ func tlsDir(t *testing.T, unixPath bool) string {
|
|||||||
return filepath.Join(getCwd(t, unixPath), "../tls")
|
return filepath.Join(getCwd(t, unixPath), "../tls")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
// 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) {
|
func runJMSTests(t *testing.T, cli ce.ContainerInterface, ID string, tls bool, user, password string, ibmjre string, cipherName string) {
|
||||||
containerConfig := container.Config{
|
port, err := cli.GetContainerPort(ID, 1414)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
containerConfig := ce.ContainerConfig{
|
||||||
// -e MQ_PORT_1414_TCP_ADDR=9.145.14.173 -e MQ_USERNAME=app -e MQ_PASSWORD=passw0rd -e MQ_CHANNEL=DEV.APP.SVRCONN -e MQ_TLS_TRUSTSTORE=/tls/test.p12 -e MQ_TLS_PASSPHRASE=passw0rd -v /Users/arthurbarr/go/src/github.com/ibm-messaging/mq-container/test/tls:/tls msgtest
|
// -e MQ_PORT_1414_TCP_ADDR=9.145.14.173 -e MQ_USERNAME=app -e MQ_PASSWORD=passw0rd -e MQ_CHANNEL=DEV.APP.SVRCONN -e MQ_TLS_TRUSTSTORE=/tls/test.p12 -e MQ_TLS_PASSPHRASE=passw0rd -v /Users/arthurbarr/go/src/github.com/ibm-messaging/mq-container/test/tls:/tls msgtest
|
||||||
Env: []string{
|
Env: []string{
|
||||||
"MQ_PORT_1414_TCP_ADDR=" + getIPAddress(t, cli, ID),
|
"MQ_PORT_1414_TCP_ADDR=127.0.0.1",
|
||||||
|
"MQ_PORT_1414_OVERRIDE=" + port,
|
||||||
"MQ_USERNAME=" + user,
|
"MQ_USERNAME=" + user,
|
||||||
"MQ_CHANNEL=DEV.APP.SVRCONN",
|
"MQ_CHANNEL=DEV.APP.SVRCONN",
|
||||||
"IBMJRE=" + os.Getenv("IBMJRE"),
|
"IBMJRE=" + ibmjre,
|
||||||
},
|
},
|
||||||
Image: imageNameDevJMS(),
|
Image: imageNameDevJMS(),
|
||||||
}
|
}
|
||||||
@@ -100,25 +118,76 @@ func runJMSTests(t *testing.T, cli *client.Client, ID string, tls bool, user, pa
|
|||||||
containerConfig.Env = append(containerConfig.Env, []string{
|
containerConfig.Env = append(containerConfig.Env, []string{
|
||||||
"MQ_TLS_TRUSTSTORE=/var/tls/client-trust.jks",
|
"MQ_TLS_TRUSTSTORE=/var/tls/client-trust.jks",
|
||||||
"MQ_TLS_PASSPHRASE=passw0rd",
|
"MQ_TLS_PASSPHRASE=passw0rd",
|
||||||
|
"MQ_TLS_CIPHER=" + cipherName,
|
||||||
}...)
|
}...)
|
||||||
}
|
}
|
||||||
hostConfig := container.HostConfig{
|
hostConfig := ce.ContainerHostConfig{
|
||||||
Binds: []string{
|
Binds: []string{
|
||||||
coverageBind(t),
|
coverageBind(t),
|
||||||
tlsDir(t, false) + ":/var/tls",
|
tlsDir(t, false) + ":/var/tls",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
networkingConfig := network.NetworkingConfig{}
|
networkingConfig := ce.ContainerNetworkSettings{
|
||||||
ctr, err := cli.ContainerCreate(context.Background(), &containerConfig, &hostConfig, &networkingConfig, strings.Replace(t.Name()+"JMS", "/", "", -1))
|
Networks: []string{"host"},
|
||||||
|
}
|
||||||
|
jmsID, err := cli.ContainerCreate(&containerConfig, &hostConfig, &networkingConfig, strings.Replace(t.Name()+"JMS", "/", "", -1))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
startContainer(t, cli, ctr.ID)
|
startContainer(t, cli, jmsID)
|
||||||
rc := waitForContainer(t, cli, ctr.ID, 2*time.Minute)
|
rc := waitForContainer(t, cli, jmsID, 2*time.Minute)
|
||||||
if rc != 0 {
|
if rc != 0 {
|
||||||
t.Errorf("JUnit container failed with rc=%v", rc)
|
t.Errorf("JUnit container failed with rc=%v", rc)
|
||||||
}
|
}
|
||||||
defer cleanContainer(t, cli, ctr.ID)
|
|
||||||
|
// Get console output of the container and process the lines
|
||||||
|
// to see if we have any failures
|
||||||
|
scanner := bufio.NewScanner(strings.NewReader(inspectLogs(t, cli, jmsID)))
|
||||||
|
for scanner.Scan() {
|
||||||
|
s := scanner.Text()
|
||||||
|
if processJunitLogLine(s) {
|
||||||
|
t.Errorf("JUnit container tests failed. Reason: %s", s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
defer cleanContainer(t, cli, jmsID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse JUnit log line and return true if line contains failed or aborted tests
|
||||||
|
func processJunitLogLine(outputLine string) bool {
|
||||||
|
var failedLine bool
|
||||||
|
// Sample JUnit test run output
|
||||||
|
//[ 2 containers found ]
|
||||||
|
//[ 0 containers skipped ]
|
||||||
|
//[ 2 containers started ]
|
||||||
|
//[ 0 containers aborted ]
|
||||||
|
//[ 2 containers successful ]
|
||||||
|
//[ 0 containers failed ]
|
||||||
|
//[ 0 tests found ]
|
||||||
|
//[ 0 tests skipped ]
|
||||||
|
//[ 0 tests started ]
|
||||||
|
//[ 0 tests aborted ]
|
||||||
|
//[ 0 tests successful ]
|
||||||
|
//[ 0 tests failed ]
|
||||||
|
|
||||||
|
// Consider only those lines that begin with '[' and with ']'
|
||||||
|
if strings.HasPrefix(outputLine, "[") && strings.HasSuffix(outputLine, "]") {
|
||||||
|
// Strip off [] and whitespaces
|
||||||
|
trimmed := strings.Trim(outputLine, "[] ")
|
||||||
|
if strings.Contains(trimmed, "aborted") || strings.Contains(trimmed, "failed") {
|
||||||
|
// Tokenize on whitespace
|
||||||
|
tokens := strings.Split(trimmed, " ")
|
||||||
|
// Determine the count of aborted or failed tests
|
||||||
|
count, err := strconv.Atoi(tokens[0])
|
||||||
|
if err == nil {
|
||||||
|
if count > 0 {
|
||||||
|
failedLine = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return failedLine
|
||||||
}
|
}
|
||||||
|
|
||||||
// createTLSConfig creates a tls.Config which trusts the specified certificate
|
// createTLSConfig creates a tls.Config which trusts the specified certificate
|
||||||
@@ -145,21 +214,31 @@ 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 ce.ContainerInterface, ID string, tlsConfig *tls.Config, errorExpected string) {
|
||||||
httpClient := http.Client{
|
httpClient := http.Client{
|
||||||
Timeout: time.Duration(30 * time.Second),
|
Timeout: time.Duration(30 * time.Second),
|
||||||
Transport: &http.Transport{
|
Transport: &http.Transport{
|
||||||
TLSClientConfig: tlsConfig,
|
TLSClientConfig: tlsConfig,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
url := fmt.Sprintf("https://localhost:%s/ibmmq/rest/v1/admin/installation", getPort(t, cli, ID, 9443))
|
port, err := cli.GetContainerPort(ID, 9443)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
url := fmt.Sprintf("https://localhost:%s/ibmmq/rest/v1/admin/installation", port)
|
||||||
req, err := http.NewRequest("GET", url, nil)
|
req, err := http.NewRequest("GET", url, nil)
|
||||||
req.SetBasicAuth("admin", defaultAdminPassword)
|
req.SetBasicAuth("admin", defaultAdminPassword)
|
||||||
resp, err := httpClient.Do(req)
|
resp, err := httpClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if len(errorExpected) > 0 {
|
||||||
|
if !strings.Contains(err.Error(), errorExpected) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if resp.StatusCode != http.StatusOK {
|
} else {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if resp != nil && resp.StatusCode != http.StatusOK {
|
||||||
t.Errorf("Expected HTTP status code %v from 'GET installation'; got %v", http.StatusOK, resp.StatusCode)
|
t.Errorf("Expected HTTP status code %v from 'GET installation'; got %v", http.StatusOK, resp.StatusCode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -182,7 +261,7 @@ func logHTTPResponse(t *testing.T, resp *http.Response) {
|
|||||||
t.Logf("HTTP response: %v", string(d))
|
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 ce.ContainerInterface, ID string, tlsConfig *tls.Config, qmName string, user string, password string, errorExpected string) {
|
||||||
httpClient := http.Client{
|
httpClient := http.Client{
|
||||||
Timeout: time.Duration(30 * time.Second),
|
Timeout: time.Duration(30 * time.Second),
|
||||||
Transport: &http.Transport{
|
Transport: &http.Transport{
|
||||||
@@ -190,7 +269,11 @@ func testRESTMessaging(t *testing.T, cli *client.Client, ID string, tlsConfig *t
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
q := "DEV.QUEUE.1"
|
q := "DEV.QUEUE.1"
|
||||||
url := fmt.Sprintf("https://localhost:%s/ibmmq/rest/v1/messaging/qmgr/%s/queue/%s/message", getPort(t, cli, ID, 9443), qmName, q)
|
port, err := cli.GetContainerPort(ID, 9443)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
url := fmt.Sprintf("https://localhost:%s/ibmmq/rest/v1/messaging/qmgr/%s/queue/%s/message", port, qmName, q)
|
||||||
putMessage := []byte("Hello")
|
putMessage := []byte("Hello")
|
||||||
req, err := http.NewRequest("POST", url, bytes.NewBuffer(putMessage))
|
req, err := http.NewRequest("POST", url, bytes.NewBuffer(putMessage))
|
||||||
req.SetBasicAuth(user, password)
|
req.SetBasicAuth(user, password)
|
||||||
@@ -199,10 +282,19 @@ func testRESTMessaging(t *testing.T, cli *client.Client, ID string, tlsConfig *t
|
|||||||
logHTTPRequest(t, req)
|
logHTTPRequest(t, req)
|
||||||
resp, err := httpClient.Do(req)
|
resp, err := httpClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if len(errorExpected) > 0 {
|
||||||
|
if strings.Contains(err.Error(), errorExpected) {
|
||||||
|
t.Logf("Error contains expected '%s' value", errorExpected)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
logHTTPResponse(t, resp)
|
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.Errorf("Expected HTTP status code %v from 'POST to queue'; got %v", http.StatusOK, resp.StatusCode)
|
||||||
t.Logf("HTTP response: %+v", resp)
|
t.Logf("HTTP response: %+v", resp)
|
||||||
t.Fail()
|
t.Fail()
|
||||||
@@ -230,3 +322,28 @@ func testRESTMessaging(t *testing.T, cli *client.Client, ID string, tlsConfig *t
|
|||||||
t.Errorf("Expected payload to be \"%s\"; got \"%s\"", putMessage, gotMessage)
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
871
test/container/docker_api_test_util.go
Normal file
871
test/container/docker_api_test_util.go
Normal file
@@ -0,0 +1,871 @@
|
|||||||
|
/*
|
||||||
|
© 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.
|
||||||
|
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 (
|
||||||
|
"archive/tar"
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"math/rand"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
ce "github.com/ibm-messaging/mq-container/test/container/containerengine"
|
||||||
|
)
|
||||||
|
|
||||||
|
func imageName() string {
|
||||||
|
image, ok := os.LookupEnv("TEST_IMAGE")
|
||||||
|
if !ok {
|
||||||
|
image = "mq-devserver:latest-x86-64"
|
||||||
|
}
|
||||||
|
return image
|
||||||
|
}
|
||||||
|
|
||||||
|
func imageNameDevJMS() string {
|
||||||
|
image, ok := os.LookupEnv("DEV_JMS_IMAGE")
|
||||||
|
if !ok {
|
||||||
|
image = "mq-dev-jms-test"
|
||||||
|
}
|
||||||
|
return image
|
||||||
|
}
|
||||||
|
|
||||||
|
// baseImage returns the ID of the underlying base image (e.g. "ubuntu" or "rhel")
|
||||||
|
func baseImage(t *testing.T, cli ce.ContainerInterface) string {
|
||||||
|
rc, out := runContainerOneShot(t, cli, "grep", "^ID=", "/etc/os-release")
|
||||||
|
if rc != 0 {
|
||||||
|
t.Fatal("Couldn't determine base image")
|
||||||
|
}
|
||||||
|
s := strings.Split(out, "=")
|
||||||
|
if len(s) < 2 {
|
||||||
|
t.Fatal("Couldn't determine base image string")
|
||||||
|
}
|
||||||
|
return s[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
// devImage returns true if the image under test is a developer image,
|
||||||
|
// determined by use of the MQ_ADMIN_PASSWORD environment variable
|
||||||
|
func devImage(t *testing.T, cli ce.ContainerInterface) bool {
|
||||||
|
rc, _ := runContainerOneShot(t, cli, "printenv", "MQ_ADMIN_PASSWORD")
|
||||||
|
if rc == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// isWSL return whether we are running in the Windows Subsystem for Linux
|
||||||
|
func isWSL(t *testing.T) bool {
|
||||||
|
if runtime.GOOS == "linux" {
|
||||||
|
uname, err := exec.Command("uname", "-r").Output()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return strings.Contains(string(uname), "Microsoft")
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// isARM returns whether we are running an arm64 MacOS machine
|
||||||
|
func isARM(t *testing.T) bool {
|
||||||
|
return runtime.GOARCH == "arm64"
|
||||||
|
}
|
||||||
|
|
||||||
|
// getCwd returns the working directory, in an os-specific or UNIX form
|
||||||
|
func getCwd(t *testing.T, unixPath bool) string {
|
||||||
|
dir, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if isWSL(t) {
|
||||||
|
// Check if the cwd is a symlink
|
||||||
|
dir, err = filepath.EvalSymlinks(dir)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !unixPath {
|
||||||
|
dir = strings.Replace(dir, getWindowsRoot(true), getWindowsRoot(false), 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dir
|
||||||
|
}
|
||||||
|
|
||||||
|
// getWindowsRoot get the path of the root directory on Windows, in UNIX or OS-specific style
|
||||||
|
func getWindowsRoot(unixStylePath bool) string {
|
||||||
|
if unixStylePath {
|
||||||
|
return "/mnt/c/"
|
||||||
|
}
|
||||||
|
return "C:/"
|
||||||
|
}
|
||||||
|
|
||||||
|
func coverage() bool {
|
||||||
|
cover := os.Getenv("TEST_COVER")
|
||||||
|
if cover == "true" || cover == "1" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// coverageDir returns the host directory to use for code coverage data
|
||||||
|
func coverageDir(t *testing.T, unixStylePath bool) string {
|
||||||
|
return filepath.Join(getCwd(t, unixStylePath), "coverage")
|
||||||
|
}
|
||||||
|
|
||||||
|
// coverageBind returns a string to use to add a bind-mounted directory for code coverage data
|
||||||
|
func coverageBind(t *testing.T) string {
|
||||||
|
return coverageDir(t, false) + ":/var/coverage"
|
||||||
|
}
|
||||||
|
|
||||||
|
// getTempDir get the path of the tmp directory, in UNIX or OS-specific style
|
||||||
|
func getTempDir(t *testing.T, unixStylePath bool) string {
|
||||||
|
if isWSL(t) {
|
||||||
|
return getWindowsRoot(unixStylePath) + "Temp/"
|
||||||
|
}
|
||||||
|
return "/tmp/"
|
||||||
|
}
|
||||||
|
|
||||||
|
// terminationMessage return the termination message, or an empty string if not set
|
||||||
|
func terminationMessage(t *testing.T, cli ce.ContainerInterface, ID string) string {
|
||||||
|
r, err := cli.CopyFromContainer(ID, "/run/termination-log")
|
||||||
|
if err != nil {
|
||||||
|
t.Log(err)
|
||||||
|
t.Log(string(r))
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return string(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func expectTerminationMessage(t *testing.T, cli ce.ContainerInterface, ID string) {
|
||||||
|
m := terminationMessage(t, cli, ID)
|
||||||
|
if m == "" {
|
||||||
|
t.Error("Expected termination message to be set")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// logContainerDetails logs selected details about the container
|
||||||
|
func logContainerDetails(t *testing.T, cli ce.ContainerInterface, ID string) {
|
||||||
|
i, err := cli.ContainerInspect(ID)
|
||||||
|
if err == nil {
|
||||||
|
d := ce.ContainerDetailsLogging{
|
||||||
|
ID: ID,
|
||||||
|
Name: i.Name,
|
||||||
|
Image: i.Image,
|
||||||
|
Path: i.Path,
|
||||||
|
Args: i.Args,
|
||||||
|
CapAdd: i.HostConfig.CapAdd,
|
||||||
|
CapDrop: i.HostConfig.CapDrop,
|
||||||
|
User: i.Config.User,
|
||||||
|
Env: i.Config.Env,
|
||||||
|
}
|
||||||
|
// If you need more details, you can always just run `json.MarshalIndent(i, "", " ")` to see everything.
|
||||||
|
t.Logf("Container details: %+v", d)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func cleanContainerQuiet(t *testing.T, cli ce.ContainerInterface, ID string) {
|
||||||
|
timeout := 10 * time.Second
|
||||||
|
err := cli.ContainerStop(ID, &timeout)
|
||||||
|
if err != nil {
|
||||||
|
// Just log the error and continue
|
||||||
|
t.Log(err)
|
||||||
|
}
|
||||||
|
opts := ce.ContainerRemoveOptions{
|
||||||
|
RemoveVolumes: true,
|
||||||
|
Force: true,
|
||||||
|
}
|
||||||
|
err = cli.ContainerRemove(ID, opts)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func cleanContainer(t *testing.T, cli ce.ContainerInterface, ID string) {
|
||||||
|
logContainerDetails(t, cli, ID)
|
||||||
|
t.Logf("Stopping container: %v", ID)
|
||||||
|
timeout := 10 * time.Second
|
||||||
|
// Stop the container. This allows the coverage output to be generated.
|
||||||
|
err := cli.ContainerStop(ID, &timeout)
|
||||||
|
if err != nil {
|
||||||
|
// Just log the error and continue
|
||||||
|
t.Log(err)
|
||||||
|
}
|
||||||
|
t.Log("Container stopped")
|
||||||
|
|
||||||
|
// If a code coverage file has been generated, then rename it to match the test name
|
||||||
|
os.Rename(filepath.Join(coverageDir(t, true), "container.cov"), filepath.Join(coverageDir(t, true), t.Name()+".cov"))
|
||||||
|
// Log the container output for any container we're about to delete
|
||||||
|
t.Logf("Console log from container %v:\n%v", ID, inspectTextLogs(t, cli, ID))
|
||||||
|
|
||||||
|
m := terminationMessage(t, cli, ID)
|
||||||
|
if m != "" {
|
||||||
|
t.Logf("Termination message: %v", m)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("Removing container: %s", ID)
|
||||||
|
opts := ce.ContainerRemoveOptions{
|
||||||
|
RemoveVolumes: true,
|
||||||
|
Force: true,
|
||||||
|
}
|
||||||
|
err = cli.ContainerRemove(ID, opts)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateRandomUID() string {
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
min := 1000
|
||||||
|
max := 9999
|
||||||
|
return fmt.Sprint(rand.Intn(max-min) + min)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getDefaultHostConfig creates a HostConfig and populates it with the defaults used in testing
|
||||||
|
func getDefaultHostConfig(t *testing.T, cli ce.ContainerInterface) *ce.ContainerHostConfig {
|
||||||
|
hostConfig := ce.ContainerHostConfig{
|
||||||
|
PortBindings: []ce.PortBinding{},
|
||||||
|
CapDrop: []string{
|
||||||
|
"ALL",
|
||||||
|
},
|
||||||
|
Privileged: false,
|
||||||
|
}
|
||||||
|
if coverage() {
|
||||||
|
hostConfig.Binds = append(hostConfig.Binds, coverageBind(t))
|
||||||
|
}
|
||||||
|
if devImage(t, cli) {
|
||||||
|
// Only needed for a RHEL-based image
|
||||||
|
if baseImage(t, cli) != "ubuntu" {
|
||||||
|
hostConfig.CapAdd = append(hostConfig.CapAdd, "DAC_OVERRIDE")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
t.Logf("Detected MQ Advanced image - dropping all capabilities")
|
||||||
|
}
|
||||||
|
return &hostConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
// runContainerWithHostConfig creates and starts a container, using the supplied HostConfig.
|
||||||
|
// Note that a default HostConfig can be created using getDefaultHostConfig.
|
||||||
|
func runContainerWithHostConfig(t *testing.T, cli ce.ContainerInterface, containerConfig *ce.ContainerConfig, hostConfig *ce.ContainerHostConfig) string {
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
networkingConfig := ce.ContainerNetworkSettings{}
|
||||||
|
t.Logf("Running container (%s)", containerConfig.Image)
|
||||||
|
ID, err := cli.ContainerCreate(containerConfig, hostConfig, &networkingConfig, t.Name())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
startContainer(t, cli, ID)
|
||||||
|
return ID
|
||||||
|
}
|
||||||
|
|
||||||
|
// runContainerWithAllConfig creates and starts a container, using the supplied ContainerConfig, HostConfig,
|
||||||
|
// NetworkingConfig, and container name (or the value of t.Name if containerName="").
|
||||||
|
func runContainerWithAllConfig(t *testing.T, cli ce.ContainerInterface, containerConfig *ce.ContainerConfig, hostConfig *ce.ContainerHostConfig, networkingConfig *ce.ContainerNetworkSettings, containerName string) string {
|
||||||
|
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)
|
||||||
|
ID, err := cli.ContainerCreate(containerConfig, hostConfig, networkingConfig, containerName)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
startContainer(t, cli, ID)
|
||||||
|
return ID
|
||||||
|
}
|
||||||
|
|
||||||
|
// runContainerWithPorts creates and starts a container, exposing the specified ports on the host.
|
||||||
|
// If no image is specified in the container config, then the image name is retrieved from the TEST_IMAGE
|
||||||
|
// environment variable.
|
||||||
|
func runContainerWithPorts(t *testing.T, cli ce.ContainerInterface, containerConfig *ce.ContainerConfig, ports []int) string {
|
||||||
|
hostConfig := getDefaultHostConfig(t, cli)
|
||||||
|
var binding ce.PortBinding
|
||||||
|
for _, p := range ports {
|
||||||
|
port := fmt.Sprintf("%v/tcp", p)
|
||||||
|
binding = ce.PortBinding{
|
||||||
|
ContainerPort: port,
|
||||||
|
HostIP: "0.0.0.0",
|
||||||
|
}
|
||||||
|
hostConfig.PortBindings = append(hostConfig.PortBindings, binding)
|
||||||
|
}
|
||||||
|
return runContainerWithHostConfig(t, cli, containerConfig, hostConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
// runContainer creates and starts a container. If no image is specified in
|
||||||
|
// the container config, then the image name is retrieved from the TEST_IMAGE
|
||||||
|
// environment variable.
|
||||||
|
func runContainer(t *testing.T, cli ce.ContainerInterface, containerConfig *ce.ContainerConfig) string {
|
||||||
|
return runContainerWithPorts(t, cli, containerConfig, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// runContainerOneShot runs a container with a custom entrypoint, as the root
|
||||||
|
// user and with default capabilities
|
||||||
|
func runContainerOneShot(t *testing.T, cli ce.ContainerInterface, command ...string) (int64, string) {
|
||||||
|
containerConfig := ce.ContainerConfig{
|
||||||
|
Entrypoint: command,
|
||||||
|
User: "root",
|
||||||
|
Image: imageName(),
|
||||||
|
}
|
||||||
|
hostConfig := ce.ContainerHostConfig{}
|
||||||
|
networkingConfig := ce.ContainerNetworkSettings{}
|
||||||
|
t.Logf("Running one shot container (%s): %v", containerConfig.Image, command)
|
||||||
|
ID, err := cli.ContainerCreate(&containerConfig, &hostConfig, &networkingConfig, t.Name()+"OneShot")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
startOptions := ce.ContainerStartOptions{}
|
||||||
|
err = cli.ContainerStart(ID, startOptions)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer cleanContainerQuiet(t, cli, ID)
|
||||||
|
rc := waitForContainer(t, cli, ID, 20*time.Second)
|
||||||
|
out := inspectLogs(t, cli, ID)
|
||||||
|
t.Logf("One shot container finished with rc=%v, output=%v", rc, out)
|
||||||
|
return rc, out
|
||||||
|
}
|
||||||
|
|
||||||
|
// runContainerOneShot runs a container with a custom entrypoint, as the root
|
||||||
|
// user, with default capabilities, and a volume mounted
|
||||||
|
func runContainerOneShotWithVolume(t *testing.T, cli ce.ContainerInterface, bind string, command ...string) (int64, string) {
|
||||||
|
containerConfig := ce.ContainerConfig{
|
||||||
|
Entrypoint: command,
|
||||||
|
User: "root",
|
||||||
|
Image: imageName(),
|
||||||
|
}
|
||||||
|
hostConfig := ce.ContainerHostConfig{
|
||||||
|
Binds: []string{
|
||||||
|
bind,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
networkingConfig := ce.ContainerNetworkSettings{}
|
||||||
|
t.Logf("Running one shot container with volume (%s): %v", containerConfig.Image, command)
|
||||||
|
ID, err := cli.ContainerCreate(&containerConfig, &hostConfig, &networkingConfig, t.Name()+"OneShotVolume")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
startOptions := ce.ContainerStartOptions{}
|
||||||
|
err = cli.ContainerStart(ID, startOptions)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer cleanContainerQuiet(t, cli, ID)
|
||||||
|
rc := waitForContainer(t, cli, ID, 20*time.Second)
|
||||||
|
out := inspectLogs(t, cli, ID)
|
||||||
|
t.Logf("One shot container finished with rc=%v, output=%v", rc, out)
|
||||||
|
return rc, out
|
||||||
|
}
|
||||||
|
|
||||||
|
func startMultiVolumeQueueManager(t *testing.T, cli ce.ContainerInterface, dataVol bool, qmsharedlogs string, qmshareddata string, env []string) (error, string, string) {
|
||||||
|
id := strconv.FormatInt(time.Now().UnixNano(), 10)
|
||||||
|
volume := createVolume(t, cli, id)
|
||||||
|
containerConfig := ce.ContainerConfig{
|
||||||
|
Image: imageName(),
|
||||||
|
Env: env,
|
||||||
|
}
|
||||||
|
var hostConfig ce.ContainerHostConfig
|
||||||
|
|
||||||
|
if !dataVol {
|
||||||
|
hostConfig = ce.ContainerHostConfig{}
|
||||||
|
} else if qmsharedlogs == "" && qmshareddata == "" {
|
||||||
|
hostConfig = getHostConfig(t, 1, "", "", volume)
|
||||||
|
} else if qmsharedlogs == "" {
|
||||||
|
hostConfig = getHostConfig(t, 2, "", qmshareddata, volume)
|
||||||
|
} else if qmshareddata == "" {
|
||||||
|
hostConfig = getHostConfig(t, 3, qmsharedlogs, "", volume)
|
||||||
|
} else {
|
||||||
|
hostConfig = getHostConfig(t, 4, qmsharedlogs, qmshareddata, volume)
|
||||||
|
}
|
||||||
|
networkingConfig := ce.ContainerNetworkSettings{}
|
||||||
|
qmID, err := cli.ContainerCreate(&containerConfig, &hostConfig, &networkingConfig, t.Name()+id)
|
||||||
|
if err != nil {
|
||||||
|
return err, "", ""
|
||||||
|
}
|
||||||
|
startContainer(t, cli, qmID)
|
||||||
|
|
||||||
|
return nil, qmID, volume
|
||||||
|
}
|
||||||
|
|
||||||
|
func getHostConfig(t *testing.T, mounts int, qmsharedlogs string, qmshareddata string, qmdata string) ce.ContainerHostConfig {
|
||||||
|
|
||||||
|
var hostConfig ce.ContainerHostConfig
|
||||||
|
|
||||||
|
switch mounts {
|
||||||
|
case 1:
|
||||||
|
hostConfig = ce.ContainerHostConfig{
|
||||||
|
Binds: []string{
|
||||||
|
qmdata + ":/mnt/mqm",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
case 2:
|
||||||
|
hostConfig = ce.ContainerHostConfig{
|
||||||
|
Binds: []string{
|
||||||
|
qmdata + ":/mnt/mqm",
|
||||||
|
qmshareddata + ":/mnt/mqm-data",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
case 3:
|
||||||
|
hostConfig = ce.ContainerHostConfig{
|
||||||
|
Binds: []string{
|
||||||
|
qmdata + ":/mnt/mqm",
|
||||||
|
qmsharedlogs + ":/mnt/mqm-log",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
case 4:
|
||||||
|
hostConfig = ce.ContainerHostConfig{
|
||||||
|
Binds: []string{
|
||||||
|
qmdata + ":/mnt/mqm",
|
||||||
|
qmsharedlogs + ":/mnt/mqm-log",
|
||||||
|
qmshareddata + ":/mnt/mqm-data",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if coverage() {
|
||||||
|
hostConfig.Binds = append(hostConfig.Binds, coverageBind(t))
|
||||||
|
}
|
||||||
|
return hostConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
func startContainer(t *testing.T, cli ce.ContainerInterface, ID string) {
|
||||||
|
t.Logf("Starting container: %v", ID)
|
||||||
|
startOptions := ce.ContainerStartOptions{}
|
||||||
|
err := cli.ContainerStart(ID, startOptions)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func stopContainer(t *testing.T, cli ce.ContainerInterface, ID string) {
|
||||||
|
t.Logf("Stopping container: %v", ID)
|
||||||
|
timeout := 10 * time.Second
|
||||||
|
err := cli.ContainerStop(ID, &timeout) //Duration(20)*time.Second)
|
||||||
|
if err != nil {
|
||||||
|
// Just log the error and continue
|
||||||
|
t.Log(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func killContainer(t *testing.T, cli ce.ContainerInterface, ID string, signal string) {
|
||||||
|
t.Logf("Killing container: %v", ID)
|
||||||
|
err := cli.ContainerKill(ID, signal)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getExitCodeFilename(t *testing.T) string {
|
||||||
|
return t.Name() + "ExitCode"
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCoverageExitCode(t *testing.T, orig int64) int64 {
|
||||||
|
f := filepath.Join(coverageDir(t, true), getExitCodeFilename(t))
|
||||||
|
_, err := os.Stat(f)
|
||||||
|
if err != nil {
|
||||||
|
t.Log(err)
|
||||||
|
return orig
|
||||||
|
}
|
||||||
|
// Remove the file, ready for the next test
|
||||||
|
defer os.Remove(f)
|
||||||
|
buf, err := ioutil.ReadFile(f)
|
||||||
|
if err != nil {
|
||||||
|
t.Log(err)
|
||||||
|
return orig
|
||||||
|
}
|
||||||
|
rc, err := strconv.Atoi(string(buf))
|
||||||
|
if err != nil {
|
||||||
|
t.Log(err)
|
||||||
|
return orig
|
||||||
|
}
|
||||||
|
t.Logf("Retrieved exit code %v from file", rc)
|
||||||
|
return int64(rc)
|
||||||
|
}
|
||||||
|
|
||||||
|
// waitForContainer waits until a container has exited
|
||||||
|
func waitForContainer(t *testing.T, cli ce.ContainerInterface, ID string, timeout time.Duration) int64 {
|
||||||
|
c, cancel := context.WithTimeout(context.Background(), timeout)
|
||||||
|
defer cancel()
|
||||||
|
t.Logf("Waiting for container for %s", timeout)
|
||||||
|
okC, errC := cli.ContainerWait(c, ID, ce.ContainerStateNotRunning)
|
||||||
|
var rc int64
|
||||||
|
select {
|
||||||
|
case err := <-errC:
|
||||||
|
t.Fatal(err)
|
||||||
|
case ok := <-okC:
|
||||||
|
rc = ok
|
||||||
|
}
|
||||||
|
if coverage() {
|
||||||
|
// COVERAGE: When running coverage, the exit code is written to a file,
|
||||||
|
// to allow the coverage to be generated (which doesn't happen for non-zero
|
||||||
|
// exit codes)
|
||||||
|
rc = getCoverageExitCode(t, rc)
|
||||||
|
}
|
||||||
|
return rc
|
||||||
|
}
|
||||||
|
|
||||||
|
// execContainer runs a command in a running container, and returns the exit code and output
|
||||||
|
func execContainer(t *testing.T, cli ce.ContainerInterface, ID string, user string, cmd []string) (int, string) {
|
||||||
|
t.Logf("Running command: %v", cmd)
|
||||||
|
exitcode, outputStr := cli.ExecContainer(ID, user, cmd)
|
||||||
|
return exitcode, outputStr
|
||||||
|
}
|
||||||
|
|
||||||
|
func waitForReady(t *testing.T, cli ce.ContainerInterface, ID string) {
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 4*time.Minute)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-time.After(1 * time.Second):
|
||||||
|
rc, _ := execContainer(t, cli, ID, "", []string{"chkmqready"})
|
||||||
|
|
||||||
|
if rc == 0 {
|
||||||
|
t.Log("MQ is ready")
|
||||||
|
return
|
||||||
|
} else if rc == 10 {
|
||||||
|
t.Log("MQ Readiness: Queue Manager Running as Standby")
|
||||||
|
return
|
||||||
|
} else if rc == 20 {
|
||||||
|
t.Log("MQ Readiness: Queue Manager Running as Replica")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case <-ctx.Done():
|
||||||
|
t.Fatal("Timed out waiting for container to become ready")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func createNetwork(t *testing.T, cli ce.ContainerInterface) string {
|
||||||
|
name := "test"
|
||||||
|
t.Logf("Creating network: %v", name)
|
||||||
|
opts := ce.NetworkCreateOptions{}
|
||||||
|
netID, err := cli.NetworkCreate(name, opts)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Logf("Created network %v with ID %v", name, netID)
|
||||||
|
return netID
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeNetwork(t *testing.T, cli ce.ContainerInterface, ID string) {
|
||||||
|
t.Logf("Removing network ID: %v", ID)
|
||||||
|
err := cli.NetworkRemove(ID)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func createVolume(t *testing.T, cli ce.ContainerInterface, name string) string {
|
||||||
|
v, err := cli.VolumeCreate(ce.VolumeCreateOptions{
|
||||||
|
Driver: "local",
|
||||||
|
Name: name,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Logf("Created volume %v", v)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeVolume(t *testing.T, cli ce.ContainerInterface, name string) {
|
||||||
|
t.Logf("Removing volume %v", name)
|
||||||
|
err := cli.VolumeRemove(name, true)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func inspectTextLogs(t *testing.T, cli ce.ContainerInterface, ID string) string {
|
||||||
|
jsonLogs := inspectLogs(t, cli, ID)
|
||||||
|
scanner := bufio.NewScanner(strings.NewReader(jsonLogs))
|
||||||
|
b := make([]byte, 64*1024)
|
||||||
|
buf := bytes.NewBuffer(b)
|
||||||
|
for scanner.Scan() {
|
||||||
|
text := scanner.Text()
|
||||||
|
if strings.HasPrefix(text, "{") {
|
||||||
|
// If it's a JSON log message, it makes it hard to debug the test, as the JSON
|
||||||
|
// is embedded in the long test output. So just summarize the JSON instead.
|
||||||
|
var e map[string]interface{}
|
||||||
|
json.Unmarshal([]byte(text), &e)
|
||||||
|
fmt.Fprintf(buf, "{\"ibm_datetime\": \"%v\", \"message\": \"%v\", ...}\n", e["ibm_datetime"], e["message"])
|
||||||
|
} else {
|
||||||
|
fmt.Fprintln(buf, text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err := scanner.Err()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func inspectLogs(t *testing.T, cli ce.ContainerInterface, ID string) string {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
logs, err := cli.GetContainerLogs(ctx, ID, ce.ContainerLogsOptions{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return logs
|
||||||
|
}
|
||||||
|
|
||||||
|
// generateTAR creates a TAR-formatted []byte, with the specified files included.
|
||||||
|
func generateTAR(t *testing.T, files []struct{ Name, Body string }) []byte {
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
tw := tar.NewWriter(buf)
|
||||||
|
for _, file := range files {
|
||||||
|
hdr := &tar.Header{
|
||||||
|
Name: file.Name,
|
||||||
|
Mode: 0600,
|
||||||
|
Size: int64(len(file.Body)),
|
||||||
|
}
|
||||||
|
err := tw.WriteHeader(hdr)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
_, err = tw.Write([]byte(file.Body))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err := tw.Close()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return buf.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
// createImage creates a new Docker image with the specified files included.
|
||||||
|
func createImage(t *testing.T, cli ce.ContainerInterface, files []struct{ Name, Body string }) string {
|
||||||
|
r := bytes.NewReader(generateTAR(t, files))
|
||||||
|
tag := strings.ToLower(t.Name())
|
||||||
|
|
||||||
|
tmpDir, err := os.MkdirTemp("", "tmp")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer os.RemoveAll(tmpDir)
|
||||||
|
|
||||||
|
//Write files to temp directory
|
||||||
|
for _, file := range files {
|
||||||
|
//Add tag to file name to allow parallel testing
|
||||||
|
f, err := os.Create(filepath.Join(tmpDir, file.Name))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
body := []byte(file.Body)
|
||||||
|
_, err = f.Write(body)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_, err = cli.ImageBuild(r, tag, filepath.Join(tmpDir, files[0].Name))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return tag
|
||||||
|
}
|
||||||
|
|
||||||
|
// deleteImage deletes a Docker image
|
||||||
|
func deleteImage(t *testing.T, cli ce.ContainerInterface, id string) {
|
||||||
|
cli.ImageRemove(id, ce.ImageRemoveOptions{
|
||||||
|
Force: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func copyFromContainer(t *testing.T, cli ce.ContainerInterface, id string, file string) []byte {
|
||||||
|
b, err := cli.CopyFromContainer(id, file)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func countLines(t *testing.T, r io.Reader) int {
|
||||||
|
scanner := bufio.NewScanner(r)
|
||||||
|
count := 0
|
||||||
|
for scanner.Scan() {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
err := scanner.Err()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
|
||||||
|
func countTarLines(t *testing.T, b []byte) int {
|
||||||
|
r := bytes.NewReader(b)
|
||||||
|
tr := tar.NewReader(r)
|
||||||
|
total := 0
|
||||||
|
for {
|
||||||
|
_, err := tr.Next()
|
||||||
|
if err == io.EOF {
|
||||||
|
// End of TAR
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
total += countLines(t, tr)
|
||||||
|
}
|
||||||
|
return total
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 ce.ContainerInterface, containerConfig *ce.ContainerConfig, hostConfig *ce.ContainerHostConfig, networkingConfig *ce.ContainerNetworkSettings, 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)
|
||||||
|
ID, err := cli.ContainerCreate(containerConfig, hostConfig, networkingConfig, containerName)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
err = startContainerError(t, cli, ID)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return ID, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func startContainerError(t *testing.T, cli ce.ContainerInterface, ID string) error {
|
||||||
|
t.Logf("Starting container: %v", ID)
|
||||||
|
startOptions := ce.ContainerStartOptions{}
|
||||||
|
err := cli.ContainerStart(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 ce.ContainerInterface, 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 ce.ContainerInterface, id string, expectedMessageId 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, expectedMessageId) {
|
||||||
|
return jsonLogs, nil
|
||||||
|
}
|
||||||
|
case <-ctx.Done():
|
||||||
|
return "", fmt.Errorf("expected message Id %s was not logged", expectedMessageId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// waitForMessageCountInLog will check for a particular message with wait and must occur exact number of times in log as specified by count
|
||||||
|
func waitForMessageCountInLog(t *testing.T, cli ce.ContainerInterface, id string, expectedMessageId string, count int) (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, expectedMessageId) && strings.Count(jsonLogs, expectedMessageId) == count {
|
||||||
|
return jsonLogs, nil
|
||||||
|
}
|
||||||
|
case <-ctx.Done():
|
||||||
|
return "", fmt.Errorf("expected message Id %s was not logged or it was not logged %v times", expectedMessageId, count)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns fully qualified path
|
||||||
|
func tlsDirDN(t *testing.T, unixPath bool, certPath string) string {
|
||||||
|
return filepath.Join(getCwd(t, unixPath), certPath)
|
||||||
|
}
|
||||||
3
test/container/go.mod
Normal file
3
test/container/go.mod
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
module github.com/ibm-messaging/mq-container/test/container
|
||||||
|
|
||||||
|
go 1.19
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
© Copyright IBM Corporation 2019, 2020
|
© Copyright IBM Corporation 2019, 2023
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -16,11 +16,12 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/docker/docker/client"
|
ce "github.com/ibm-messaging/mq-container/test/container/containerengine"
|
||||||
)
|
)
|
||||||
|
|
||||||
var miEnv = []string{
|
var miEnv = []string{
|
||||||
@@ -33,10 +34,7 @@ var miEnv = []string{
|
|||||||
// and starts/stop them checking we always have an active and standby
|
// and starts/stop them checking we always have an active and standby
|
||||||
func TestMultiInstanceStartStop(t *testing.T) {
|
func TestMultiInstanceStartStop(t *testing.T) {
|
||||||
t.Skipf("Skipping %v until test defect fixed", t.Name())
|
t.Skipf("Skipping %v until test defect fixed", t.Name())
|
||||||
cli, err := client.NewEnvClient()
|
cli := ce.NewContainerClient()
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
err, qm1aId, qm1bId, volumes := configureMultiInstance(t, cli)
|
err, qm1aId, qm1bId, volumes := configureMultiInstance(t, cli)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -75,10 +73,7 @@ func TestMultiInstanceStartStop(t *testing.T) {
|
|||||||
// TestMultiInstanceContainerStop starts 2 containers in a multi instance queue manager configuration,
|
// TestMultiInstanceContainerStop starts 2 containers in a multi instance queue manager configuration,
|
||||||
// stops the active queue manager, then checks to ensure the backup queue manager becomes active
|
// stops the active queue manager, then checks to ensure the backup queue manager becomes active
|
||||||
func TestMultiInstanceContainerStop(t *testing.T) {
|
func TestMultiInstanceContainerStop(t *testing.T) {
|
||||||
cli, err := client.NewEnvClient()
|
cli := ce.NewContainerClient()
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
err, qm1aId, qm1bId, volumes := configureMultiInstance(t, cli)
|
err, qm1aId, qm1bId, volumes := configureMultiInstance(t, cli)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -92,15 +87,28 @@ func TestMultiInstanceContainerStop(t *testing.T) {
|
|||||||
waitForReady(t, cli, qm1aId)
|
waitForReady(t, cli, qm1aId)
|
||||||
waitForReady(t, cli, qm1bId)
|
waitForReady(t, cli, qm1bId)
|
||||||
|
|
||||||
err, active, standby := getActiveStandbyQueueManager(t, cli, qm1aId, qm1bId)
|
err, originalActive, originalStandby := getActiveStandbyQueueManager(t, cli, qm1aId, qm1bId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
stopContainer(t, cli, active)
|
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
stopContainer(t, cli, originalActive)
|
||||||
|
|
||||||
if status := getQueueManagerStatus(t, cli, standby, "QM1"); strings.Compare(status, "Running") != 0 {
|
for {
|
||||||
t.Fatalf("Expected QM1 to be running as active queue manager, dspmq returned status of %v", status)
|
status := getQueueManagerStatus(t, cli, originalStandby, "QM1")
|
||||||
|
select {
|
||||||
|
case <-time.After(1 * time.Second):
|
||||||
|
if status == "Running" {
|
||||||
|
t.Logf("Original standby is now the active")
|
||||||
|
return
|
||||||
|
} else if status == "Starting" {
|
||||||
|
t.Logf("Original standby is starting")
|
||||||
|
}
|
||||||
|
case <-ctx.Done():
|
||||||
|
t.Fatalf("%s Timed out waiting for standby to become the active. Status=%v", time.Now().Format(time.RFC3339), status)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,21 +116,16 @@ func TestMultiInstanceContainerStop(t *testing.T) {
|
|||||||
// configuration, then checks to ensure that both an active and standby queue manager have been started
|
// configuration, then checks to ensure that both an active and standby queue manager have been started
|
||||||
func TestMultiInstanceRace(t *testing.T) {
|
func TestMultiInstanceRace(t *testing.T) {
|
||||||
t.Skipf("Skipping %v until file lock is implemented", t.Name())
|
t.Skipf("Skipping %v until file lock is implemented", t.Name())
|
||||||
|
cli := ce.NewContainerClient()
|
||||||
cli, err := client.NewEnvClient()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
qmsharedlogs := createVolume(t, cli, "qmsharedlogs")
|
qmsharedlogs := createVolume(t, cli, "qmsharedlogs")
|
||||||
defer removeVolume(t, cli, qmsharedlogs.Name)
|
defer removeVolume(t, cli, qmsharedlogs)
|
||||||
qmshareddata := createVolume(t, cli, "qmshareddata")
|
qmshareddata := createVolume(t, cli, "qmshareddata")
|
||||||
defer removeVolume(t, cli, qmshareddata.Name)
|
defer removeVolume(t, cli, qmshareddata)
|
||||||
|
|
||||||
qmsChannel := make(chan QMChan)
|
qmsChannel := make(chan QMChan)
|
||||||
|
|
||||||
go singleMultiInstanceQueueManager(t, cli, qmsharedlogs.Name, qmshareddata.Name, qmsChannel)
|
go singleMultiInstanceQueueManager(t, cli, qmsharedlogs, qmshareddata, qmsChannel)
|
||||||
go singleMultiInstanceQueueManager(t, cli, qmsharedlogs.Name, qmshareddata.Name, qmsChannel)
|
go singleMultiInstanceQueueManager(t, cli, qmsharedlogs, qmshareddata, qmsChannel)
|
||||||
|
|
||||||
qm1a := <-qmsChannel
|
qm1a := <-qmsChannel
|
||||||
if qm1a.Error != nil {
|
if qm1a.Error != nil {
|
||||||
@@ -145,7 +148,7 @@ func TestMultiInstanceRace(t *testing.T) {
|
|||||||
waitForReady(t, cli, qm1aId)
|
waitForReady(t, cli, qm1aId)
|
||||||
waitForReady(t, cli, qm1bId)
|
waitForReady(t, cli, qm1bId)
|
||||||
|
|
||||||
err, _, _ = getActiveStandbyQueueManager(t, cli, qm1aId, qm1bId)
|
err, _, _ := getActiveStandbyQueueManager(t, cli, qm1aId, qm1bId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -155,10 +158,7 @@ func TestMultiInstanceRace(t *testing.T) {
|
|||||||
// mounts, then checks to ensure that the container terminates with the expected message
|
// mounts, then checks to ensure that the container terminates with the expected message
|
||||||
func TestMultiInstanceNoSharedMounts(t *testing.T) {
|
func TestMultiInstanceNoSharedMounts(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
cli, err := client.NewEnvClient()
|
cli := ce.NewContainerClient()
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err, qm1aId, qm1aData := startMultiVolumeQueueManager(t, cli, true, "", "", miEnv)
|
err, qm1aId, qm1aData := startMultiVolumeQueueManager(t, cli, true, "", "", miEnv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -174,15 +174,12 @@ func TestMultiInstanceNoSharedMounts(t *testing.T) {
|
|||||||
// TestMultiInstanceNoSharedLogs starts 2 multi instance queue managers without providing a shared log
|
// TestMultiInstanceNoSharedLogs starts 2 multi instance queue managers without providing a shared log
|
||||||
// mount, then checks to ensure that the container terminates with the expected message
|
// mount, then checks to ensure that the container terminates with the expected message
|
||||||
func TestMultiInstanceNoSharedLogs(t *testing.T) {
|
func TestMultiInstanceNoSharedLogs(t *testing.T) {
|
||||||
cli, err := client.NewEnvClient()
|
cli := ce.NewContainerClient()
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
qmshareddata := createVolume(t, cli, "qmshareddata")
|
qmshareddata := createVolume(t, cli, "qmshareddata")
|
||||||
defer removeVolume(t, cli, qmshareddata.Name)
|
defer removeVolume(t, cli, qmshareddata)
|
||||||
|
|
||||||
err, qm1aId, qm1aData := startMultiVolumeQueueManager(t, cli, true, "", qmshareddata.Name, miEnv)
|
err, qm1aId, qm1aData := startMultiVolumeQueueManager(t, cli, true, "", qmshareddata, miEnv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -196,15 +193,12 @@ func TestMultiInstanceNoSharedLogs(t *testing.T) {
|
|||||||
// TestMultiInstanceNoSharedData starts 2 multi instance queue managers without providing a shared data
|
// TestMultiInstanceNoSharedData starts 2 multi instance queue managers without providing a shared data
|
||||||
// mount, then checks to ensure that the container terminates with the expected message
|
// mount, then checks to ensure that the container terminates with the expected message
|
||||||
func TestMultiInstanceNoSharedData(t *testing.T) {
|
func TestMultiInstanceNoSharedData(t *testing.T) {
|
||||||
cli, err := client.NewEnvClient()
|
cli := ce.NewContainerClient()
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
qmsharedlogs := createVolume(t, cli, "qmsharedlogs")
|
qmsharedlogs := createVolume(t, cli, "qmsharedlogs")
|
||||||
defer removeVolume(t, cli, qmsharedlogs.Name)
|
defer removeVolume(t, cli, qmsharedlogs)
|
||||||
|
|
||||||
err, qm1aId, qm1aData := startMultiVolumeQueueManager(t, cli, true, qmsharedlogs.Name, "", miEnv)
|
err, qm1aId, qm1aData := startMultiVolumeQueueManager(t, cli, true, qmsharedlogs, "", miEnv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -218,10 +212,7 @@ func TestMultiInstanceNoSharedData(t *testing.T) {
|
|||||||
// TestMultiInstanceNoMounts starts 2 multi instance queue managers without providing a shared data
|
// TestMultiInstanceNoMounts starts 2 multi instance queue managers without providing a shared data
|
||||||
// mount, then checks to ensure that the container terminates with the expected message
|
// mount, then checks to ensure that the container terminates with the expected message
|
||||||
func TestMultiInstanceNoMounts(t *testing.T) {
|
func TestMultiInstanceNoMounts(t *testing.T) {
|
||||||
cli, err := client.NewEnvClient()
|
cli := ce.NewContainerClient()
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err, qm1aId, qm1aData := startMultiVolumeQueueManager(t, cli, false, "", "", miEnv)
|
err, qm1aId, qm1aData := startMultiVolumeQueueManager(t, cli, false, "", "", miEnv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
© Copyright IBM Corporation 2019
|
© Copyright IBM Corporation 2019, 2023
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -23,7 +23,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/docker/docker/client"
|
ce "github.com/ibm-messaging/mq-container/test/container/containerengine"
|
||||||
)
|
)
|
||||||
|
|
||||||
type QMChan struct {
|
type QMChan struct {
|
||||||
@@ -34,27 +34,27 @@ type QMChan struct {
|
|||||||
|
|
||||||
// configureMultiInstance creates the volumes and containers required for basic testing
|
// configureMultiInstance creates the volumes and containers required for basic testing
|
||||||
// of multi instance queue managers. Returns error, qm1a ID, qm1b ID, slice of volume names
|
// of multi instance queue managers. Returns error, qm1a ID, qm1b ID, slice of volume names
|
||||||
func configureMultiInstance(t *testing.T, cli *client.Client) (error, string, string, []string) {
|
func configureMultiInstance(t *testing.T, cli ce.ContainerInterface) (error, string, string, []string) {
|
||||||
|
|
||||||
qmsharedlogs := createVolume(t, cli, "qmsharedlogs")
|
qmsharedlogs := createVolume(t, cli, "qmsharedlogs")
|
||||||
qmshareddata := createVolume(t, cli, "qmshareddata")
|
qmshareddata := createVolume(t, cli, "qmshareddata")
|
||||||
|
|
||||||
err, qm1aId, qm1aData := startMultiVolumeQueueManager(t, cli, true, qmsharedlogs.Name, qmshareddata.Name, miEnv)
|
err, qm1aId, qm1aData := startMultiVolumeQueueManager(t, cli, true, qmsharedlogs, qmshareddata, miEnv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err, "", "", []string{}
|
return err, "", "", []string{}
|
||||||
}
|
}
|
||||||
time.Sleep(10 * time.Second)
|
time.Sleep(10 * time.Second)
|
||||||
err, qm1bId, qm1bData := startMultiVolumeQueueManager(t, cli, true, qmsharedlogs.Name, qmshareddata.Name, miEnv)
|
err, qm1bId, qm1bData := startMultiVolumeQueueManager(t, cli, true, qmsharedlogs, qmshareddata, miEnv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err, "", "", []string{}
|
return err, "", "", []string{}
|
||||||
}
|
}
|
||||||
|
|
||||||
volumes := []string{qmsharedlogs.Name, qmshareddata.Name, qm1aData, qm1bData}
|
volumes := []string{qmsharedlogs, qmshareddata, qm1aData, qm1bData}
|
||||||
|
|
||||||
return nil, qm1aId, qm1bId, volumes
|
return nil, qm1aId, qm1bId, volumes
|
||||||
}
|
}
|
||||||
|
|
||||||
func singleMultiInstanceQueueManager(t *testing.T, cli *client.Client, qmsharedlogs string, qmshareddata string, qmsChannel chan QMChan) {
|
func singleMultiInstanceQueueManager(t *testing.T, cli ce.ContainerInterface, qmsharedlogs string, qmshareddata string, qmsChannel chan QMChan) {
|
||||||
err, qmId, qmData := startMultiVolumeQueueManager(t, cli, true, qmsharedlogs, qmshareddata, miEnv)
|
err, qmId, qmData := startMultiVolumeQueueManager(t, cli, true, qmsharedlogs, qmshareddata, miEnv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
qmsChannel <- QMChan{Error: err}
|
qmsChannel <- QMChan{Error: err}
|
||||||
@@ -62,7 +62,7 @@ func singleMultiInstanceQueueManager(t *testing.T, cli *client.Client, qmsharedl
|
|||||||
qmsChannel <- QMChan{QMId: qmId, QMData: qmData}
|
qmsChannel <- QMChan{QMId: qmId, QMData: qmData}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getActiveStandbyQueueManager(t *testing.T, cli *client.Client, qm1aId string, qm1bId string) (error, string, string) {
|
func getActiveStandbyQueueManager(t *testing.T, cli ce.ContainerInterface, qm1aId string, qm1bId string) (error, string, string) {
|
||||||
qm1aStatus := getQueueManagerStatus(t, cli, qm1aId, "QM1")
|
qm1aStatus := getQueueManagerStatus(t, cli, qm1aId, "QM1")
|
||||||
qm1bStatus := getQueueManagerStatus(t, cli, qm1bId, "QM1")
|
qm1bStatus := getQueueManagerStatus(t, cli, qm1bId, "QM1")
|
||||||
|
|
||||||
@@ -75,15 +75,16 @@ func getActiveStandbyQueueManager(t *testing.T, cli *client.Client, qm1aId strin
|
|||||||
return err, "", ""
|
return err, "", ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func getQueueManagerStatus(t *testing.T, cli *client.Client, containerID string, queueManagerName string) string {
|
func getQueueManagerStatus(t *testing.T, cli ce.ContainerInterface, containerID string, queueManagerName string) string {
|
||||||
_, dspmqOut := execContainer(t, cli, containerID, "", []string{"bash", "-c", "dspmq", "-m", queueManagerName})
|
_, dspmqOut := execContainer(t, cli, containerID, "", []string{"bash", "-c", "dspmq", "-m", queueManagerName})
|
||||||
|
t.Logf("dspmq for %v (%v) returned: %v", containerID, queueManagerName, dspmqOut)
|
||||||
regex := regexp.MustCompile(`STATUS\(.*\)`)
|
regex := regexp.MustCompile(`STATUS\(.*\)`)
|
||||||
status := regex.FindString(dspmqOut)
|
status := regex.FindString(dspmqOut)
|
||||||
status = strings.TrimSuffix(strings.TrimPrefix(status, "STATUS("), ")")
|
status = strings.TrimSuffix(strings.TrimPrefix(status, "STATUS("), ")")
|
||||||
return status
|
return status
|
||||||
}
|
}
|
||||||
|
|
||||||
func waitForTerminationMessage(t *testing.T, cli *client.Client, qmId string, terminationString string, timeout time.Duration) {
|
func waitForTerminationMessage(t *testing.T, cli ce.ContainerInterface, qmId string, terminationString string, timeout time.Duration) {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
for {
|
for {
|
||||||
@@ -92,7 +93,7 @@ func waitForTerminationMessage(t *testing.T, cli *client.Client, qmId string, te
|
|||||||
m := terminationMessage(t, cli, qmId)
|
m := terminationMessage(t, cli, qmId)
|
||||||
if m != "" {
|
if m != "" {
|
||||||
if !strings.Contains(m, terminationString) {
|
if !strings.Contains(m, terminationString) {
|
||||||
t.Fatalf("Expected container to fail on missing required mount. Got termination message: %v", m)
|
t.Fatalf("Expected container to fail with termination message %v. Got termination message: %v", terminationString, m)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
285
test/container/mq_native_ha_test.go
Normal file
285
test/container/mq_native_ha_test.go
Normal file
@@ -0,0 +1,285 @@
|
|||||||
|
/*
|
||||||
|
© Copyright IBM Corporation 2021, 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 (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
ce "github.com/ibm-messaging/mq-container/test/container/containerengine"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestNativeHABasic creates 3 containers in a Native HA queue manager configuration
|
||||||
|
// and ensures the queue manger and replicas start as expected
|
||||||
|
func TestNativeHABasic(t *testing.T) {
|
||||||
|
cli := ce.NewContainerClient()
|
||||||
|
|
||||||
|
version, err := cli.GetMQVersion(imageName())
|
||||||
|
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{}
|
||||||
|
qmVolumes := []string{}
|
||||||
|
//Each native HA qmgr instance is exposed on subsequent ports on the host starting with basePort
|
||||||
|
//If the qmgr exposes more than one port (tests do not do this currently) then they are offset by +50
|
||||||
|
basePort := 14551
|
||||||
|
for i := 0; i <= 2; i++ {
|
||||||
|
nhaPort := basePort + i
|
||||||
|
vol := createVolume(t, cli, containerNames[i])
|
||||||
|
defer removeVolume(t, cli, vol)
|
||||||
|
qmVolumes = append(qmVolumes, vol)
|
||||||
|
containerConfig := getNativeHAContainerConfig(containerNames[i], containerNames, basePort)
|
||||||
|
hostConfig := getHostConfig(t, 1, "", "", vol)
|
||||||
|
hostConfig = populateNativeHAPortBindings([]int{9414}, nhaPort, hostConfig)
|
||||||
|
networkingConfig := getNativeHANetworkConfig("host")
|
||||||
|
ctr := runContainerWithAllConfig(t, cli, &containerConfig, &hostConfig, &networkingConfig, containerNames[i])
|
||||||
|
defer cleanContainer(t, cli, ctr)
|
||||||
|
qmReplicaIDs[i] = ctr
|
||||||
|
}
|
||||||
|
|
||||||
|
waitForReadyHA(t, cli, qmReplicaIDs)
|
||||||
|
|
||||||
|
_, err = getActiveReplicaInstances(t, cli, qmReplicaIDs)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestNativeHAFailover creates 3 containers in a Native HA queue manager configuration,
|
||||||
|
// stops the active queue manager, checks a replica becomes active, and ensures the stopped
|
||||||
|
// queue manager comes back as a replica
|
||||||
|
func TestNativeHAFailover(t *testing.T) {
|
||||||
|
|
||||||
|
cli := ce.NewContainerClient()
|
||||||
|
|
||||||
|
version, err := cli.GetMQVersion(imageName())
|
||||||
|
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{}
|
||||||
|
qmVolumes := []string{}
|
||||||
|
//Each native HA qmgr instance is exposed on subsequent ports on the host starting with basePort
|
||||||
|
//If the qmgr exposes more than one port (tests do not do this currently) then they are offset by +50
|
||||||
|
basePort := 14551
|
||||||
|
for i := 0; i <= 2; i++ {
|
||||||
|
nhaPort := basePort + i
|
||||||
|
vol := createVolume(t, cli, containerNames[i])
|
||||||
|
defer removeVolume(t, cli, vol)
|
||||||
|
qmVolumes = append(qmVolumes, vol)
|
||||||
|
containerConfig := getNativeHAContainerConfig(containerNames[i], containerNames, basePort)
|
||||||
|
hostConfig := getHostConfig(t, 1, "", "", vol)
|
||||||
|
hostConfig = populateNativeHAPortBindings([]int{9414}, nhaPort, hostConfig)
|
||||||
|
networkingConfig := getNativeHANetworkConfig("host")
|
||||||
|
ctr := runContainerWithAllConfig(t, cli, &containerConfig, &hostConfig, &networkingConfig, containerNames[i])
|
||||||
|
defer cleanContainer(t, cli, ctr)
|
||||||
|
qmReplicaIDs[i] = ctr
|
||||||
|
}
|
||||||
|
|
||||||
|
waitForReadyHA(t, cli, qmReplicaIDs)
|
||||||
|
|
||||||
|
haStatus, err := getActiveReplicaInstances(t, cli, qmReplicaIDs)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
stopContainer(t, cli, haStatus.Active)
|
||||||
|
waitForFailoverHA(t, cli, haStatus.Replica)
|
||||||
|
startContainer(t, cli, haStatus.Active)
|
||||||
|
waitForReady(t, cli, haStatus.Active)
|
||||||
|
|
||||||
|
_, 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 enabled, and ensures the queue manger and replicas start as expected
|
||||||
|
func TestNativeHASecure(t *testing.T) {
|
||||||
|
cli := ce.NewContainerClient()
|
||||||
|
|
||||||
|
version, err := cli.GetMQVersion(imageName())
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
if isARM(t) {
|
||||||
|
t.Skip("Skipping as an issue has been identified for the arm64 MQ image")
|
||||||
|
}
|
||||||
|
|
||||||
|
containerNames := [3]string{"QM1_1", "QM1_2", "QM1_3"}
|
||||||
|
qmReplicaIDs := [3]string{}
|
||||||
|
//Each native HA qmgr instance is exposed on subsequent ports on the host starting with basePort
|
||||||
|
//If the qmgr exposes more than one port (tests do not do this currently) then they are offset by +50
|
||||||
|
basePort := 14551
|
||||||
|
for i := 0; i <= 2; i++ {
|
||||||
|
nhaPort := basePort + i
|
||||||
|
containerConfig := getNativeHAContainerConfig(containerNames[i], containerNames, defaultHAPort)
|
||||||
|
containerConfig.Env = append(containerConfig.Env, "MQ_NATIVE_HA_TLS=true")
|
||||||
|
hostConfig := getNativeHASecureHostConfig(t)
|
||||||
|
hostConfig = populateNativeHAPortBindings([]int{9414}, nhaPort, hostConfig)
|
||||||
|
networkingConfig := getNativeHANetworkConfig("host")
|
||||||
|
ctr := runContainerWithAllConfig(t, cli, &containerConfig, &hostConfig, &networkingConfig, containerNames[i])
|
||||||
|
defer cleanContainer(t, cli, ctr)
|
||||||
|
qmReplicaIDs[i] = ctr
|
||||||
|
}
|
||||||
|
|
||||||
|
waitForReadyHA(t, cli, qmReplicaIDs)
|
||||||
|
|
||||||
|
_, 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 enabled, overrides the default CipherSpec, and ensures the queue manger
|
||||||
|
// and replicas start as expected
|
||||||
|
func TestNativeHASecureCipherSpec(t *testing.T) {
|
||||||
|
cli := ce.NewContainerClient()
|
||||||
|
|
||||||
|
version, err := cli.GetMQVersion(imageName())
|
||||||
|
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{}
|
||||||
|
//Each native HA qmgr instance is exposed on subsequent ports on the host starting with basePort
|
||||||
|
//If the qmgr exposes more than one port (tests do not do this currently) then they are offset by +50
|
||||||
|
basePort := 14551
|
||||||
|
for i := 0; i <= 2; i++ {
|
||||||
|
nhaPort := basePort + i
|
||||||
|
containerConfig := getNativeHAContainerConfig(containerNames[i], containerNames, defaultHAPort)
|
||||||
|
containerConfig.Env = append(containerConfig.Env, "MQ_NATIVE_HA_TLS=true", "MQ_NATIVE_HA_CIPHERSPEC=TLS_AES_256_GCM_SHA384")
|
||||||
|
hostConfig := getNativeHASecureHostConfig(t)
|
||||||
|
hostConfig = populateNativeHAPortBindings([]int{9414}, nhaPort, hostConfig)
|
||||||
|
networkingConfig := getNativeHANetworkConfig("host")
|
||||||
|
ctr := runContainerWithAllConfig(t, cli, &containerConfig, &hostConfig, &networkingConfig, containerNames[i])
|
||||||
|
defer cleanContainer(t, cli, ctr)
|
||||||
|
qmReplicaIDs[i] = ctr
|
||||||
|
}
|
||||||
|
|
||||||
|
waitForReadyHA(t, cli, qmReplicaIDs)
|
||||||
|
|
||||||
|
_, 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, 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 := ce.NewContainerClient()
|
||||||
|
|
||||||
|
version, err := cli.GetMQVersion(imageName())
|
||||||
|
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{}
|
||||||
|
//Each native HA qmgr instance is exposed on subsequent ports on the host starting with basePort
|
||||||
|
//If the qmgr exposes more than one port (tests do not do this currently) then they are offset by +50
|
||||||
|
basePort := 14551
|
||||||
|
for i := 0; i <= 2; i++ {
|
||||||
|
nhaPort := basePort + 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)
|
||||||
|
hostConfig = populateNativeHAPortBindings([]int{9414}, nhaPort, hostConfig)
|
||||||
|
networkingConfig := getNativeHANetworkConfig("host")
|
||||||
|
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 := ce.NewContainerClient()
|
||||||
|
|
||||||
|
version, err := cli.GetMQVersion(imageName())
|
||||||
|
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{}
|
||||||
|
//Each native HA qmgr instance is exposed on subsequent ports on the host starting with basePort
|
||||||
|
//If the qmgr exposes more than one port (tests do not do this currently) then they are offset by +50
|
||||||
|
basePort := 14551
|
||||||
|
for i := 0; i <= 2; i++ {
|
||||||
|
nhaPort := basePort + 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=SSL_ECDHE_ECDSA_WITH_RC4_128_SHA", "MQ_ENABLE_FIPS=true")
|
||||||
|
hostConfig := getNativeHASecureHostConfig(t)
|
||||||
|
hostConfig = populateNativeHAPortBindings([]int{9414}, nhaPort, hostConfig)
|
||||||
|
networkingConfig := getNativeHANetworkConfig("host")
|
||||||
|
ctr := runContainerWithAllConfig(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
|
||||||
|
qmReplicaIDs[i] = ctr
|
||||||
|
}
|
||||||
|
for i := 0; i <= 2; i++ {
|
||||||
|
waitForTerminationMessage(t, cli, qmReplicaIDs[i], "/opt/mqm/bin/strmqm: exit status 23", 60*time.Second)
|
||||||
|
}
|
||||||
|
}
|
||||||
157
test/container/mq_native_ha_test_util.go
Normal file
157
test/container/mq_native_ha_test_util.go
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
/*
|
||||||
|
© Copyright IBM Corporation 2021, 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 (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
ce "github.com/ibm-messaging/mq-container/test/container/containerengine"
|
||||||
|
)
|
||||||
|
|
||||||
|
const defaultHAPort = 9414
|
||||||
|
|
||||||
|
// HAReplicaStatus represents the Active/Replica/Replica container status of the queue manager
|
||||||
|
type HAReplicaStatus struct {
|
||||||
|
Active string
|
||||||
|
Replica [2]string
|
||||||
|
}
|
||||||
|
|
||||||
|
func getNativeHAContainerConfig(containerName string, replicaNames [3]string, haPort int) ce.ContainerConfig {
|
||||||
|
return ce.ContainerConfig{
|
||||||
|
Env: []string{
|
||||||
|
"LICENSE=accept",
|
||||||
|
"MQ_QMGR_NAME=QM1",
|
||||||
|
"AMQ_CLOUD_PAK=true",
|
||||||
|
"MQ_NATIVE_HA=true",
|
||||||
|
fmt.Sprintf("HOSTNAME=%s", containerName),
|
||||||
|
fmt.Sprintf("MQ_NATIVE_HA_INSTANCE_0_NAME=%s", replicaNames[0]),
|
||||||
|
fmt.Sprintf("MQ_NATIVE_HA_INSTANCE_1_NAME=%s", replicaNames[1]),
|
||||||
|
fmt.Sprintf("MQ_NATIVE_HA_INSTANCE_2_NAME=%s", replicaNames[2]),
|
||||||
|
fmt.Sprintf("MQ_NATIVE_HA_INSTANCE_0_REPLICATION_ADDRESS=%s(%d)", "127.0.0.1", haPort+0),
|
||||||
|
fmt.Sprintf("MQ_NATIVE_HA_INSTANCE_1_REPLICATION_ADDRESS=%s(%d)", "127.0.0.1", haPort+1),
|
||||||
|
fmt.Sprintf("MQ_NATIVE_HA_INSTANCE_2_REPLICATION_ADDRESS=%s(%d)", "127.0.0.1", haPort+2),
|
||||||
|
},
|
||||||
|
//When using the host for networking a consistent user was required. If a random user is used then the following example error was recorded.
|
||||||
|
//AMQ3209E: Native HA connection rejected due to configuration mismatch of 'QmgrUserId=5024'
|
||||||
|
User: "1111",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getNativeHASecureHostConfig(t *testing.T) ce.ContainerHostConfig {
|
||||||
|
return ce.ContainerHostConfig{
|
||||||
|
Binds: []string{
|
||||||
|
coverageBind(t),
|
||||||
|
filepath.Join(getCwd(t, true), "../tls") + ":/etc/mqm/ha/pki/keys/ha",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getNativeHANetworkConfig(networkID string) ce.ContainerNetworkSettings {
|
||||||
|
return ce.ContainerNetworkSettings{
|
||||||
|
Networks: []string{networkID},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// populatePortBindings writes port bindings to the host config
|
||||||
|
func populateNativeHAPortBindings(ports []int, nativeHaPort int, hostConfig ce.ContainerHostConfig) ce.ContainerHostConfig {
|
||||||
|
hostConfig.PortBindings = []ce.PortBinding{}
|
||||||
|
var binding ce.PortBinding
|
||||||
|
for i, p := range ports {
|
||||||
|
port := fmt.Sprintf("%v/tcp", p)
|
||||||
|
binding = ce.PortBinding{
|
||||||
|
ContainerPort: port,
|
||||||
|
HostIP: "0.0.0.0",
|
||||||
|
//Offset the ports by 50 if there are multiple
|
||||||
|
HostPort: strconv.Itoa(nativeHaPort + 50*i),
|
||||||
|
}
|
||||||
|
hostConfig.PortBindings = append(hostConfig.PortBindings, binding)
|
||||||
|
}
|
||||||
|
return hostConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
func getActiveReplicaInstances(t *testing.T, cli ce.ContainerInterface, qmReplicaIDs [3]string) (HAReplicaStatus, error) {
|
||||||
|
|
||||||
|
var actives []string
|
||||||
|
var replicas []string
|
||||||
|
|
||||||
|
for _, id := range qmReplicaIDs {
|
||||||
|
qmReplicaStatus := getQueueManagerStatus(t, cli, id, "QM1")
|
||||||
|
if qmReplicaStatus == "Running" {
|
||||||
|
actives = append(actives, id)
|
||||||
|
} else if qmReplicaStatus == "Replica" {
|
||||||
|
replicas = append(replicas, id)
|
||||||
|
} else {
|
||||||
|
err := fmt.Errorf("Expected status to be Running or Replica, got status: %s", qmReplicaStatus)
|
||||||
|
return HAReplicaStatus{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(actives) != 1 || len(replicas) != 2 {
|
||||||
|
err := fmt.Errorf("Expected 1 Active and 2 Replicas, got: %d Active and %d Replica", len(actives), len(replicas))
|
||||||
|
return HAReplicaStatus{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return HAReplicaStatus{actives[0], [2]string{replicas[0], replicas[1]}}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func waitForReadyHA(t *testing.T, cli ce.ContainerInterface, qmReplicaIDs [3]string) {
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 4*time.Minute)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-time.After(1 * time.Second):
|
||||||
|
for _, id := range qmReplicaIDs {
|
||||||
|
rc, _ := execContainer(t, cli, id, "", []string{"chkmqready"})
|
||||||
|
if rc == 0 {
|
||||||
|
t.Log("MQ is ready")
|
||||||
|
rc, _ := execContainer(t, cli, id, "", []string{"chkmqstarted"})
|
||||||
|
if rc == 0 {
|
||||||
|
t.Log("MQ has started")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case <-ctx.Done():
|
||||||
|
t.Fatal("Timed out waiting for HA Queue Manager to become ready")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func waitForFailoverHA(t *testing.T, cli ce.ContainerInterface, replicas [2]string) {
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-time.After(1 * time.Second):
|
||||||
|
for _, id := range replicas {
|
||||||
|
if status := getQueueManagerStatus(t, cli, id, "QM1"); status == "Running" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case <-ctx.Done():
|
||||||
|
t.Fatal("Timed out waiting for Native HA Queue Manager to failover to an available replica")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user