Compare commits

...

236 Commits
9.2.0 ... 9.3.0

Author SHA1 Message Date
Tom Jefferson
7d14e60c24 Merge pull request #445 from mq-cloudpak/tadj-go-update-lts
Update go version (LTS)
2023-05-25 14:56:26 +01:00
Tom Jefferson
4e1393e166 Update go version (LTS) 2023-05-25 14:30:08 +01:00
Tom Jefferson
c6b9a9c948 Merge pull request #444 from mq-cloudpak/tadj-update-june-lts
Update release to 9.3.0.5-r3 (June 2.0.12 release)
2023-05-25 10:40:49 +01:00
Tom Jefferson
09427ff5e8 Update release to 9.3.0.5-r3 (June) 2023-05-24 17:45:14 +01:00
Tom Jefferson
3c7a2bc962 Merge pull request #439 from mq-cloudpak/tadj-update-go-mod-lts
Update go mod to 1.19
2023-05-18 09:32:09 +01:00
Tom Jefferson
e00858fd73 Update go mod to 1.19 2023-05-17 15:26:53 +01:00
Tom Jefferson
594ba29e56 Merge pull request #434 from mq-cloudpak/tadj-update-go-20x
Update GO and UBI (LTS)
2023-05-17 11:27:27 +01:00
Tom Jefferson
c614db01b7 Update GO and UBI (LTS) 2023-05-17 10:30:48 +01:00
Tom Jefferson
8257b9634d Merge pull request #429 from mq-cloudpak/tadj-update-go-lts
Update go version
2023-05-05 11:50:53 +01:00
Tom Jefferson
6d624ae33b Update go version 2023-05-05 09:54:02 +01:00
Tom Jefferson
2cb6cf3a4d Merge pull request #426 from mq-cloudpak/tadj-update-may-version
Update release for 9.3.0.5-r2 (May 2.0.11 release)
2023-05-04 12:51:38 +01:00
Tom Jefferson
c90c95fc12 Update release for 9.3.0.5-r2 (May 2.0.11 release) 2023-05-04 11:18:12 +01:00
Alec Painter
a88063aa5c Merge pull request #423 from mq-cloudpak/ahp-9.3.0.5-april
updated missed go version
2023-04-11 13:58:10 +01:00
Alec Painter
d655ad283b Merge branch 'v9.3.0.x' into ahp-9.3.0.5-april 2023-04-11 13:00:55 +01:00
Alec-Painter
0e66afa78b updated missed go version 2023-04-11 12:57:25 +01:00
Alec Painter
fd527fb30a Merge pull request #418 from mq-cloudpak/ahp-9.3.0.5-april
Updated to MQ 9.3.0.5-r1
2023-04-06 13:23:55 +01:00
Alec-Painter
d6d837928f Updated to MQ 9.3.0.5-r1 2023-04-06 11:02:59 +01:00
Tom Jefferson
a28c8a3acb Update version to 9.3.0.4-r2. Update go to 1.18 and re-vendor (#411)
* Update version to 9.3.0.4-r2. Update go to 1.18 and re-vendor dependencies
2023-03-02 13:37:17 +00:00
Tom Jefferson
4311c04634 Merge pull request #376 from mq-cloudpak/amf-1831-version-length-930
Handle multi-digit portions of VRMF
2023-02-24 12:05:51 +00:00
Tom Jefferson
5ff3c358ed Merge branch 'v9.3.0.x' into amf-1831-version-length-930 2023-02-24 09:56:21 +00:00
Tom Jefferson
880fa699ef Merge pull request #404 from mq-cloudpak/tadj-9.3.0.4-update
Update version to 9.3.0.4-r1 and update ubi
2023-02-08 16:26:23 +00:00
Tom Jefferson
bd7c7cd91d Update version to 9.3.0.4-r1 and update ubi 2023-02-08 13:05:07 +00:00
Tom Jefferson
fb2e7c71e1 Update gosec behaviour and version (#397)
* Update gosec behaviour and fix resulting gosec vulnerabilities (#399)

Co-authored-by: KIRAN DARBHA <kirandarbha@in.ibm.com>
2023-02-07 22:03:13 +00:00
Alex Mirski-Fitton
923407e77f Fix setting buildkit disable environment variable 2023-02-06 14:43:43 +00:00
Simon Hirst
2181258e38 Updating gosec to 2.14.0
Updating gosec to 2.14.0
2023-02-01 14:41:02 +00:00
Simon Hirst
015481bd3e Updating gosec to 2.14.0 2023-02-01 12:46:28 +00:00
Alex Mirski-Fitton
27dada90d6 Handle multi-digit portions of VRMF 2023-01-24 13:34:08 +00:00
Alec Painter
de2d068bb6 Merge pull request #368 from mq-cloudpak/sjh-dec-lts-update
Version updates for January release
2023-01-11 08:23:14 +00:00
Simon Hirst
fd34ea0a78 Updating UBI and changelog 2023-01-09 15:07:51 +00:00
Simon Hirst
ad02f98858 Updating versions for January LTS release 2023-01-09 15:02:22 +00:00
Tom Jefferson
27679a6c58 Merge pull request #355 from mq-cloudpak/sjh-dec-updates
Updates for December LTS release
2022-12-06 17:57:38 +00:00
Simon Hirst
592ebdf555 Updating .travis.yml and Dockerfile-server UBI version and RELEASE 2022-12-06 15:32:53 +00:00
Alec Painter
35101841cc Merge pull request #348 from mq-cloudpak/ahp-9301-ubi
updated go-version & ubi
2022-11-14 10:34:39 +00:00
Alec-Painter
9033fd67d7 updated go-version & ubi 2022-11-14 10:11:38 +00:00
Alec Painter
066db7bf04 Merge pull request #341 from mq-cloudpak/ahp-nov-ga-9301r3
update release version for 9.3.0.1-r3
2022-10-21 12:07:29 +01:00
Alec-Painter
c64c188ae5 update release version for 9.3.0.1-r3 2022-10-21 09:06:27 +01:00
Tom Jefferson
8889ced3c8 Merge pull request #338 from mq-cloudpak/tadj-ipgate-go-toolset-930x
Update go toolset
2022-10-10 15:57:40 +01:00
Tom Jefferson
c4bf74bdf5 Update go toolset 2022-10-10 15:08:27 +01:00
Tom Jefferson
090a761aef Merge pull request #332 from mq-cloudpak/tadj-create-9.3.0.1-r2
Update ubi/go/release for 9.3.0.1-r2
2022-09-28 20:05:17 +01:00
Tom Jefferson
11d6685c45 Update ubi/go/release for 9.3.0.1-r2 2022-09-28 19:44:37 +01:00
Alec Painter
45934d8a59 Merge pull request #324 from mq-cloudpak/ahp-9.3.0.1-r1
Updated changelog for 9.3.0.1
2022-09-14 11:25:10 +01:00
Alec Painter
c9de739331 Merge branch 'v9.3.0.x' into ahp-9.3.0.1-r1 2022-09-14 10:37:27 +01:00
Alec-Painter
c78bc602b0 Updated changelog for 9.3.0.1 2022-09-14 10:31:07 +01:00
Alec Painter
f78ac15820 Merge pull request #323 from mq-cloudpak/ahp-9.3.0.1-r1
* Added MQ 9.3.0.1-r1

* Updated ubi & go-toolset versions
2022-09-12 12:15:27 +01:00
Alec-Painter
0e05030047 updated ubi & go version 2022-09-12 11:20:04 +01:00
Alec-Painter
46869e4df4 updated to version 9.3.0.1-r1 2022-09-12 11:18:12 +01:00
Alex Mirski-Fitton
8ba8ae4f6c Merge pull request #313 from mq-cloudpak/amf-credential-helper-go-version-930
Pin docker-credential-helpers for old go installs
2022-08-22 13:25:11 +01:00
Alex Mirski-Fitton
03e939a49b Pin docker-credential-helpers for old go installs 2022-08-22 11:49:54 +01:00
Tom Jefferson
f60281688e Merge pull request #310 from mq-cloudpak/tadj-update-ubi-930x
Update ubi and go version
2022-08-09 14:02:43 +01:00
Tom Jefferson
c3a23b7a1b Update ubi and go version 2022-08-08 21:34:09 +01:00
Prerna Srivastava
1941fc1675 Merge pull request #300 from mq-cloudpak/WS_fixes
Ws fixes
2022-08-05 14:24:32 +05:30
Prerna Srivastava
167319022f WS fix 2022-08-05 13:46:47 +05:30
Prerna Srivastava
fb1d17764a ws fixes 2022-08-05 12:04:04 +05:30
Tom Jefferson
5a20b85759 Merge pull request #294 from mq-cloudpak/tadj-add-august-cd-version
Update release for 9.3.0.0-r3 release
2022-08-01 15:59:29 +01:00
Tom Jefferson
d4668af82f Update release for 9.3.0.0-r3 release 2022-08-01 15:22:09 +01:00
Simon Hirst
ce8a34b1bb Push fake master to different namespace 2022-08-01 14:29:29 +01:00
Simon Hirst
f9852fb553 Push fake master to different namespace 2022-08-01 14:03:32 +01:00
Alex Mirski-Fitton
3957d487c3 Merge pull request #270 from mq-cloudpak/amf-9300r2
Update base images for 9.3.0.0-r2 release
2022-07-01 14:36:45 +01:00
Alex Mirski-Fitton
6427e0d84b Update base images for 9.3.0.0-r2 release 2022-07-01 11:54:21 +01:00
Alex Mirski-Fitton
9f12fc5a04 [ci skip]: Setting up v9.3.0.x branch 2022-06-28 15:46:05 +01:00
Alex Mirski-Fitton
813e1ac2dc [ci skip] Update branch name for 9.2.0 LTS (#269) 2022-06-28 15:45:21 +01:00
Alec Painter
3111d48330 Merge pull request #266 from mq-cloudpak/ahp-master-ubi
updated ubi & go toolset
2022-06-15 11:56:48 +01:00
Alec Painter
b8dcbde7b7 updated ubi & go toolset 2022-06-15 10:44:29 +01:00
David Bell
c74cc13a3b [ci skip]: Update building doc link (#262) 2022-06-10 20:41:00 +01:00
arthur.barr@uk.ibm.com
35cc716fcb Update CHANGELOG for 9.3.0 2022-06-08 14:34:35 +01:00
arthur.barr@uk.ibm.com
163873d7a8 Update default TLS cipher for dev config to use TLS12 or higher
The default cipher for the default developer config is ANY_TLS12. This restricts TLS communications to those channels to just TLS 1.2 ciphers and so does not allow people to connect clients with TLS 1.3. This is unnecessarily restrictive and so we should use ANY_TLS12_OR_HIGHER instead.
2022-06-06 13:13:21 +01:00
arthur.barr@uk.ibm.com
0e18f17dc9 Faster build without separate SDK install
Before this change, only the MQ SDK was installed into the go-toolset image, for use at build time.  The genmqpkg command could take around a minute.
2022-05-30 15:47:00 +01:00
arthur.barr@uk.ibm.com
d6ea28ee6b Fix build warning by removing unused variable 2022-05-30 15:47:00 +01:00
David Bell
093c6be85a Merge pull request #254 from mq-cloudpak/drb-remove-extra-lts-builds
Remove extra LTS build from travis
2022-05-27 19:53:12 +01:00
David Bell
28faa252a2 Remove extra LTS build from travis 2022-05-27 13:31:19 +01:00
David Bell
334df22cfd Merge pull request #253 from mq-cloudpak/drb-fixlink
fix doc link
2022-05-27 09:04:35 +01:00
David Bell
b32963854b fix doc link 2022-05-26 20:15:45 +01:00
David Bell
ad153a3fc2 Merge pull request #252 from mq-cloudpak/drb-LTS-build-doc
update building doc for 9.3
2022-05-26 20:12:39 +01:00
David Bell
caa0fd6904 update building doc for 9.3 2022-05-26 16:10:03 +01:00
David Bell
bd7e1193bf Merge pull request #250 from mq-cloudpak/drbsjh-dontpushlts
dont push LTS images to artifactory
2022-05-19 09:00:56 +01:00
David Bell
7c4d95aa2d dont push LTS images to artifactory 2022-05-18 21:15:04 +01:00
Tom Jefferson
7f8ffbf914 Merge pull request #249 from mq-cloudpak/tadj-update-lts-release
Update LTS Version
2022-05-13 10:51:54 +01:00
Tom Jefferson
d3c543a42e Merge pull request #246 from mq-cloudpak/tadj-ubi-go-buffer
Update UBI/Go and add buffer to signals
2022-05-13 10:39:34 +01:00
Tom Jefferson
4931e43b67 Update LTS Version 2022-05-13 10:10:54 +01:00
Tom Jefferson
4e26150542 Update UBI/Go and add buffer to signals 2022-05-12 17:03:47 +01:00
arthur.barr@uk.ibm.com
bf3d8dd26d Use web server build for Podman on macOS 2022-05-12 15:55:10 +01:00
arthur.barr@uk.ibm.com
7c58e2bea2 Make the build faster
Re-use the go-toolset builder image which has the MQ SDK installed, for the C builder image, instead of re-installing the MQ SDK.

Also reduced the number of layers, as each layer was adding time to the build.
2022-05-12 13:48:52 +01:00
KIRAN DARBHA
ae5b736f40 Updating .whitesource file for v9.2.5 (#238) 2022-05-06 16:20:53 +05:30
Tom Jefferson
c1b092e0b1 Merge pull request #236 from mq-cloudpak/tj-update-ubi
Update ubi and go version
2022-05-03 17:33:39 +01:00
Tom Jefferson
adf7582e8b Update ubi and go version 2022-05-03 16:43:46 +01:00
arthur.barr@uk.ibm.com
544c2d1e41 Upgrade Docker API and JUnit 2022-04-28 13:01:32 +01:00
Stephen Marshall
c3f60c5e24 Add default jvm.options file 2022-04-27 12:52:14 +01:00
arthur.barr@uk.ibm.com
b16246455e Remove use of EXTRA_ARGS in Makefile 2022-04-14 15:01:50 +01:00
arthur.barr@uk.ibm.com
fdc447761c Handle failure to download files with curl 2022-04-14 15:01:50 +01:00
arthur.barr@uk.ibm.com
7f5563fa97 Clean up docker network on build failure 2022-04-14 15:01:50 +01:00
arthur.barr@uk.ibm.com
767381b2a0 Change README to reference new sample Helm chart 2022-04-14 11:13:35 +01:00
arthur.barr@uk.ibm.com
3ad3e7ea16 Use icr.io in usage doc 2022-04-14 11:13:35 +01:00
arthur.barr@uk.ibm.com
f6fbc71092 Switch to registry.access.redhat.com registry 2022-04-14 11:13:35 +01:00
Alec Painter
0943d420bc Merge pull request #227 from mq-cloudpak/ahp-ubi-master
Updated UBI
2022-04-12 15:27:35 +01:00
Alec Painter
48cac4fb6c updated ubi 2022-04-12 13:53:33 +01:00
Stephen Marshall
c56ec8cd79 Update to MQ 9.3.0.0 2022-03-29 16:32:43 +01:00
Tom Jefferson
748d2fd11f Merge pull request #221 from mq-cloudpak/tadj-ubi-updates
UBI/Go Update
2022-03-16 13:53:36 +00:00
Tom Jefferson
24eb903b56 UBI/Go Update 2022-03-16 10:06:19 +00:00
Prerna Srivastava
ebbe30ccf9 Merge pull request #220 from mq-cloudpak/WS-14mar22
Ws 14mar22
2022-03-15 16:43:41 +05:30
Prerna Srivastava
65e5d60984 version update 2022-03-14 15:44:20 +05:30
Prerna Srivastava
2279f0b33c Go Version update# 2022-03-14 14:52:14 +05:30
Tom Jefferson
8fa8d8cb2a Merge pull request #217 from mq-cloudpak/tadj-ubi-update
Update ubi and go versions
2022-03-11 11:28:28 +00:00
Tom Jefferson
40d3a9e9ce Update ubi and go versions 2022-03-11 10:30:48 +00:00
KIRAN DARBHA
6a8dcfae79 WSS fixes (#209)
* Random changes

* Updated latest version

* Version update

* Version update

* Version update

* Version update

* updated modules.ext file

* fixing build

Co-authored-by: Prerna Srivastava <prernasrivastava@Prernas-MacBook-Pro.local>
2022-02-28 14:54:13 +05:30
Simon Hirst
85976e1c08 Merge pull request #207 from mq-cloudpak/sjh/remote-qm-fix
Disabling remote queue managers
2022-02-17 15:04:51 +00:00
Simon Hirst
4bf3c81e4e Disabling remote queue managers 2022-02-17 12:45:39 +00:00
David Bell
61e94ed50c Merge pull request #205 from mq-cloudpak/ahp-925-ubis
Updated UBI + go versions
2022-02-04 15:09:12 +00:00
Alec Painter
5c964ef5f7 Updated UBI + go versions 2022-02-04 12:46:30 +00:00
Alec Painter
8db0023815 Merge pull request #202 from mq-cloudpak/ahp-v1.8-ubi
Updated UBI + golang toolset versions to latest for private-master
2022-01-20 16:44:10 +00:00
Alec Painter
060a2e9655 Updated go version in travis file 2022-01-20 16:07:13 +00:00
Alec Painter
d7595f46ff Updated UBI + golang toolset versions to latest 2022-01-20 14:07:50 +00:00
arthur.barr@uk.ibm.com
d2c11089c8 Improve Makefile for podman and SELinux 2022-01-20 10:41:02 +00:00
arthur.barr@uk.ibm.com
d7fd217770 Tolerate nix configuration files 2022-01-20 10:41:02 +00:00
Tom Jefferson
00ada50f06 Fix incorrect variable name 2022-01-13 20:29:31 +00:00
Tom Jefferson
5d88af462f Only lookup level if prereqs met (#197) 2022-01-13 16:31:54 +00:00
Tom Jefferson
eff6ded259 Add MQ build level to the container labels (#195)
* Changes to add MQ build level to the container labels

* Add jq as a dependency
2022-01-13 10:59:02 +00:00
Stephen Marshall
2f103128f3 Fix bug in Makefile for Power support 2021-12-06 20:37:05 +00:00
Stephen Marshall
a48ac18522 Power support (#193)
* MQ 9.2.5.0 & Power Support
2021-12-06 17:51:50 +00:00
Alex Mirski-Fitton
1f6d37afed Merge pull request #190 from mq-cloudpak/amf-update-go-924
Update go, go-toolset, and UBI
2021-11-12 16:18:00 +00:00
Alex Mirski-Fitton
5f706e0282 Update go, go-toolset, and UBI 2021-11-11 16:32:18 +00:00
Alex Mirski-Fitton
1a8855547b Merge pull request #189 from mq-cloudpak/amf-update-go-924
Update ubi-minimal and go-toolset level
2021-11-03 11:31:51 +00:00
Alex Mirski-Fitton
d0062e71e8 Update ubi-minimal and go-toolset level 2021-11-03 10:21:29 +00:00
Alex Mirski-Fitton
353a671c27 Merge pull request #187 from mq-cloudpak/amf-update-go-pm
Update go
2021-10-28 08:56:35 +01:00
Alex Mirski-Fitton
a46177893c Update go 2021-10-27 14:25:48 +01:00
David Bell
65c0ddb289 Merge pull request #185 from mq-cloudpak/drb-investigate-linux
pickup latest 924
2021-10-08 10:34:45 +01:00
David Bell
78203aaead pickup latest 924 2021-10-08 09:33:22 +01:00
Alex Mirski-Fitton
7cba9ee95f Merge pull request #182 from mq-cloudpak/amf-update-dependencies
Update base images
2021-09-28 13:32:51 +01:00
KIRAN DARBHA
7e6b01825b Merge pull request #181 from mq-cloudpak/WS-eusupdate
updating WS to report issues for eus branch
2021-09-28 13:37:45 +05:30
Alex Mirski-Fitton
79d79ca3d6 Move builder image to args 2021-09-27 20:21:06 +01:00
Alex Mirski-Fitton
1e415a30aa Update ubi-minimal and go-toolset image references 2021-09-27 20:20:46 +01:00
Alex Mirski-Fitton
8461c8822a Merge pull request #180 from mq-cloudpak/jason
Push any image requested, ie do not skip ppcle ones
2021-09-27 17:04:59 +01:00
kirandarbha
eac4363913 formatting json block 2021-09-27 19:21:16 +05:30
kirandarbha
a3bf84bcea updating WS to report issues for eus branch 2021-09-27 12:20:17 +05:30
Jason Edmeades
8284ac3169 Copyright update 2021-09-24 16:25:36 +01:00
Jason Edmeades
68ef36fafb Push any image requested, ie do not skip ppcle ones 2021-09-24 16:16:39 +01:00
David Bell
1af99634b4 Merge pull request #179 from mq-cloudpak/amf-9.2.4-release
Update to MQ 9.2.4 CD
2021-09-08 11:08:05 +01:00
Alex Mirski-Fitton
53353c6bac Update LTS to 9.2.0.2-r2 2021-09-08 10:25:28 +01:00
Alex Mirski-Fitton
a73303d6e6 Update LTS version to 9.2.0.2 in Travis 2021-09-07 10:56:14 +01:00
Alex Mirski-Fitton
18b0458fa5 Update travis go version to match go-toolset 2021-09-06 17:36:20 +01:00
Alex Mirski-Fitton
44eeda157a Target 9.2.4.0 release 2021-09-06 17:33:14 +01:00
Alex Mirski-Fitton
062b29960f Handle window between cont. start & port binding 2021-09-06 17:29:58 +01:00
Alex Mirski-Fitton
d4e38c6d85 Add common name to SAN list for test cert 2021-09-06 17:02:21 +01:00
arthur.barr@uk.ibm.com
25ba610c6a Don't use LogFilePages in INI test, as it's now disallowed 2021-08-12 17:32:56 +01:00
KIRAN DARBHA
6d2ff0933a Merge pull request #175 from mq-cloudpak/whitesource/configure
Configure WhiteSource for GitHub Enterprise
2021-07-29 17:52:52 +05:30
whitesource-ets[bot]
7a6afb4654 Add .whitesource configuration file 2021-07-29 08:57:42 +00:00
David Bell
f5fdd1008f Merge pull request #172 from mq-cloudpak/newubigocat
update ubi go
2021-07-05 10:45:03 +01:00
David Bell
0857c654d2 update ubi go 2021-07-05 10:06:47 +01:00
Arthur Barr
480c3ee027 Use ubi8/openjdk-8 instead of docker.io/maven for JMS tests 2021-06-30 15:19:10 +01:00
Prerna Srivastava
54076af43d Update base image (#168)
Update ubi and go toolset
2021-06-14 14:35:44 +01:00
Stephen Marshall
608f255ab0 Update README with 9.2.3.0 license links 2021-06-10 11:54:34 +01:00
Luke Powlett
a927b6e01e Trust HA replication certificates in the HA CMS keystore 2021-05-18 17:43:02 +01:00
Luke Powlett
987a7657ce Removed INSYNC check for Native HA startup probe 2021-05-12 15:12:14 +01:00
Luke Powlett
94a78b3122 Authenticate with docker.io if user is set 2021-05-05 17:38:23 +01:00
Luke Powlett
fcc458bf31 Updated to MQ 9.2.3 CD 2021-05-05 17:38:23 +01:00
David R Bell
14987c1170 Merge pull request #153 from mq-cloudpak/drb-dev922
pickup 922 dev drivers
2021-03-11 21:25:36 +00:00
David Bell
46b0ceede3 pickup 922 dev drivers 2021-03-11 15:50:01 +00:00
Luke J Powlett
4bbb447e74 Updated UBI and golang fix versions 2021-03-09 14:16:08 +00:00
Nathaniel J King
ebbbbb04ab Merge pull request #151 from mq-cloudpak/zenone
Small change to handle Zen SSO
2021-03-09 09:09:51 +00:00
nathking@uk.ibm.com
54bad805c1 Small change to handle Zen SSO 2021-03-08 21:38:00 +00:00
Paras Mamgain
cecade9845 Merge pull request #149 from mq-cloudpak/revert-2
reverting dev licese to developer
2021-03-04 12:44:26 +05:30
mamgainp
f61a08fe50 reverting dev licese to developer 2021-03-04 11:39:35 +05:30
Stephen Marshall
573fbb8fb1 Fix developer license link 2021-02-24 12:15:46 +00:00
Luke J Powlett
e99c07192d Added batch option on gpg key for RSYNC to support bionic 2021-02-15 10:01:57 +00:00
Luke Powlett
9252465cab Added option to enable MQ trace for crtmqm 2021-02-04 17:19:51 +00:00
Stephen D Marshall
a778eb1cc9 Release updates (#142)
* 9.2.2.0 release updates
2021-02-04 10:21:55 +00:00
Stephen D Marshall
2bf2052248 Update dspmq command for chkmqstarted (#141) 2021-02-03 14:17:20 +00:00
Luke J Powlett
d903336fe0 Merge pull request #140 from mq-cloudpak/native-ha
Native HA
2021-02-02 18:24:15 +00:00
Luke Powlett
b7dcff0bbc Set main branch back to private-master for merge 2021-02-02 17:38:55 +00:00
Stephen Marshall
3e6ceb4bb9 Merge with latest code from master 2021-01-27 10:45:33 +00:00
David R Bell
b2767947a0 resolve docker push (#139) 2021-01-25 10:23:56 +00:00
Arthur Barr
a24258834e Better error handling in htpasswd
CSP handling is now separate, and the MQ return codes are tidied up.
Also added defaultIdentityTest to JMS tests and fixed copyright dates for htpasswd code
2021-01-25 08:52:32 +00:00
Arthur Barr
76070234d4 Optionally mirror web logs to stdout 2021-01-25 08:52:32 +00:00
Arthur Barr
d2ea17ec30 Use MQ 9.2 client in tests 2021-01-25 08:52:32 +00:00
Arthur Barr
c0e05be791 Don't use Docker BuildKit 2021-01-25 08:52:32 +00:00
Arthur Barr
12a2dee175 Add hostname to log 2021-01-25 08:52:32 +00:00
Arthur Barr
ac3dcdd0d0 Further changes with htpasswd provider 2021-01-25 08:52:32 +00:00
Arthur Barr
4257f6a199 Improvements to htpasswd code following review
Improved multi-threading, including new test
2021-01-25 08:52:32 +00:00
Arthur Barr
5fd9fc5e26 Convert HTPasswd code from Go to C 2021-01-25 08:52:32 +00:00
David R Bell
adbc95c5d5 Merge pull request #136 from mq-cloudpak/drb-bionic
moving to bionic
2021-01-22 09:38:22 +00:00
David Bell
99a1e4aa74 moving to bionic 2021-01-21 13:34:18 +00:00
Stephen D Marshall
2ae82d71d6 Chkmqstarted (#135)
* Add chkmqstarted

* Fix go.sum

* Fix bug & extra unit-test check

* Clean-up StartupProbe output
2021-01-21 11:13:08 +00:00
Luke Powlett
8a2faf2955 Added initial set of NativeHA docker tests, updated HA TLS env 2021-01-15 09:05:55 +00:00
Luke Powlett
1d41f4b138 Added NativeHA TLS keystore 2021-01-15 09:05:55 +00:00
Luke Powlett
e1c96655b1 Setup native-ha branch as main branch 2020-12-16 17:22:50 +00:00
Stephen Marshall
68fe4a1dc1 Update ready check for native-HA 2020-12-16 12:00:29 +00:00
Stephen Marshall
adf15b7bd3 Update to MQ 9.2.2.0 2020-12-16 12:00:29 +00:00
Stephen Marshall
e77ac9617d Add new native HA feature 2020-12-16 12:00:28 +00:00
David R Bell
54824879c5 Merge pull request #132 from mq-cloudpak/drb-ubigo
Update UBI and Go builder
2020-12-16 11:12:55 +00:00
David Bell
bf8580248b Update UBI and Go builder 2020-12-16 10:48:36 +00:00
Amrit K Kandola
e1978541db add variable in mqwebuser.xml to fix the webconsole issue (#131)
* add variable in mqwebuser.xml to fix the webconsole issue

* add fix to the other mqwebuser.xml files
2020-12-14 17:09:04 +00:00
Stephen Marshall
aa7580aa5a Set Web-Console CCDT values 2020-12-14 10:39:04 +00:00
Paras Mamgain
4366cd8c81 Merge pull request #129 from mq-cloudpak/MQ-921-upgrade
updating release version to r1
2020-12-04 18:01:46 +05:30
mamgainp
2a5242a4d9 updating release version to r1 2020-12-04 17:37:50 +05:30
Paras Mamgain
a4dc545a4f Merge pull request #128 from mq-cloudpak/MQ-921-upgrade
updating mq 9200 to 9210
2020-12-04 15:37:02 +05:30
VENKATA KIRAN KUMAR DARBHA
f16ce5e4a4 Merge pull request #112 from mq-cloudpak/UserExternal
User external
2020-12-04 15:13:21 +05:30
mamgainp
03cdf67439 Merge branch 'UserExternal' into MQ-921-upgrade 2020-12-04 09:53:58 +05:30
mamgainp
e8e86dcc92 updating mq 9200 to 9210 2020-12-02 12:11:55 +05:30
Arthur Barr
abf969a64d Use docker.io/golang due to CVEs in Red Hat version 2020-11-24 12:00:34 +00:00
Luke Powlett
34a55135fb Always add release tag to travis build images 2020-11-24 09:01:49 +00:00
Luke J Powlett
dd1d534045 Added support for internal build levels
* Dummy change to see if things are working

* Initial internal build support

* Add SSH key support

* Add SSH key support pt 2

* Add SSH key support pt 3

* Add Push support

* Full package support

* Correct typo

* Correct typo 2

* Disable some tests as per master

* Change rsync variable names

* Fix build issues

* Remove the -full package support for now

Co-authored-by: Jason Edmeades <jason_edmeades@uk.ibm.com>
2020-11-17 17:26:00 +00:00
Arthur Barr
c39a532da9 Use Red Hat registry for all images in build 2020-11-17 17:04:18 +00:00
Amrit K Kandola
efb35ff1b0 update ubi image (#122) 2020-11-17 12:52:47 +00:00
Nathaniel J King
c2b8753c76 Merge pull request #120 from mq-cloudpak/njknewrelease
Remove release-candidate code
2020-11-16 15:30:35 +00:00
Nathaniel J. King
f8ae8b0be1 Remove release-candidate rc tagging 2020-11-16 11:40:26 +00:00
Arthur Barr
e8d26aa79e Don't use setuid on chkmq*
Also add new tests for chkmqhealthy and privileges
2020-11-12 17:41:05 +00:00
kirandarbha
a3e3b0d8c6 update version-check condition 2020-11-03 15:20:13 +05:30
kirandarbha
23f31b1639 updating to use mqversion util 2020-11-03 10:54:13 +05:30
kirandarbha
8d164340c6 Merge branch 'private-master' into UserExternal 2020-11-03 10:06:23 +05:30
Luke J Powlett
94ad66661e Added mqversion util functions for version feature toggles 2020-11-02 17:30:21 +00:00
kirandarbha
f39f90728f adding newly introduced UserExternal to crtmqm
Removing redundant env variable in dockerfile

userexternal only if gt 9201

restore env variable for UNKNOWN_ID
2020-11-02 18:01:54 +05:30
Luke Powlett
a7125b7700 Added support for MQ LTS container builds 2020-10-29 13:11:23 +00:00
Amrit K Kandola
9f50b0efaa force xenial (#114)
force xenial to be used for travis builds so gpg generating key works.
2020-10-27 16:06:34 +00:00
Luke Powlett
0d5ed76979 Use separate namespace for rc builds 2020-10-26 14:57:46 +00:00
Daniel J Morley
c37f8f1c4c Merge pull request #111 from mq-cloudpak/update-travis
Update MQ version in travis script
2020-10-19 13:11:59 +01:00
Stephen Marshall
f74777e498 Update MQ version in travis script 2020-10-19 12:21:52 +01:00
Luke J Powlett
55c094a58a Added remote global build tag/cache for multi-arch builds 2020-10-15 09:30:11 +01:00
Stephen Marshall
3d6199e0d8 Add new target for updating MQ version 2020-10-13 10:47:33 +01:00
Luke Powlett
8111761c4f Added tagcache for image tag 2020-10-08 15:58:31 +01:00
Luke Powlett
4fcdb50928 Use travis branch instead of current branch 2020-10-08 15:58:31 +01:00
Luke Powlett
23eba2524e Tag master builds with build id 2020-10-07 17:23:19 +01:00
Amrit K Kandola
678a62f152 Freshgomod (#106)
* initial go modules, fresh start to find breaking change

* change dep to go mod vendor

* main go modules done, tests passed locally

* upgrade go in dockerfileserver
2020-10-06 19:28:48 +01:00
Luke Powlett
a2940a4ba8 Updated to latest UBI version 2020-09-15 10:53:00 +01:00
Luke Powlett
70def702b8 Updated to go 1.13.15, switched to community goloang image 2020-09-08 16:43:19 +01:00
VENKATA KIRAN KUMAR DARBHA
f72dc51475 Merge pull request #102 from mq-cloudpak/fat-manifest
fat-manifest updates
2020-08-21 18:10:46 +05:30
Thomas J Apter
afe8aba912 Merge pull request #100 from mq-cloudpak/ta_DocChanges
MQ Container Documentation Changes
2020-08-21 10:37:17 +01:00
KIRAN DARBHA
444cadf864 addressing review comments 2020-08-21 14:40:42 +05:30
KIRAN DARBHA
c2d46d1dff fat-manifest updates 2020-08-21 13:53:48 +05:30
KIRAN DARBHA
6808af107b fat-manifest updates
fat-manifest updates

fat-manifest updates

Making create-manifest-list.sh executable

fixing travis failure

fixing fat-manifests

fat-manifest

fat-manifest

fat-manifest updates

fat-manifests

fat-manifest updates

manifest-list updates

fat-manifest

updating fat-manifests

fat-manifests
2020-08-21 11:59:01 +05:30
VENKATA KIRAN KUMAR DARBHA
720ecae5bb Merge pull request #101 from mq-cloudpak/zlinux-update
adding zlinux-support
2020-08-18 20:29:30 +05:30
KIRAN DARBHA
490055e74c addressing review comments 2020-08-18 19:03:54 +05:30
KIRAN DARBHA
461529dd98 adding zlinux-support 2020-08-18 17:22:26 +05:30
KIRAN DARBHA
8d7085c18f adding zlinux-support 2020-08-18 16:49:01 +05:30
Thomas-Apter
b84581c7ee Changed testming.md to use make advancedserver 2020-08-18 10:13:08 +01:00
Thomas-Apter
b9dd2f5e79 Create advancedserver make target, and update docs to use 'make advancedserver' 2020-08-18 10:10:39 +01:00
Thomas-Apter
12bcf8b2da Changed 'ran' to 'run' 2020-08-17 15:56:42 +01:00
Thomas-Apter
64a66f6590 Reformatted testing.md and adding required step make build-devjmstest to Docker tests instructions 2020-08-17 14:57:31 +01:00
1478 changed files with 147286 additions and 272540 deletions

7
.gitignore vendored
View File

@@ -13,3 +13,10 @@ vendor/github.com/prometheus/client_model/.project
vendor/github.com/prometheus/client_model/.settings* vendor/github.com/prometheus/client_model/.settings*
gosec_results.json gosec_results.json
internal/qmgrauth/qmgroam/patch internal/qmgrauth/qmgroam/patch
.tagcache
# Nix
*.nix
.envrc
.direnv
result

View File

@@ -12,63 +12,81 @@
# 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.
# Temporarily removing dist tag as not supported by power build dist: bionic
# dist: xenial group: beta
sudo: required sudo: required
language: go language: go
go: go:
- "1.12" - "1.19.9"
services: services:
- docker - docker
env:
global:
- MAIN_BRANCH=v9.3.0.x
- TAGCACHE_FILE=tagcache
- RELEASE=r3
go_import_path: "github.com/ibm-messaging/mq-container" go_import_path: "github.com/ibm-messaging/mq-container"
# cache: # cache:
# directories: # directories:
# - downloads # - downloads
env:
global:
- RELEASE=""
jobs: jobs:
include: include:
- stage: build - stage: basic-build
name: "Basic build" if: branch != v9.3.0.x AND tag IS blank
if: branch != private-master AND tag IS blank 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_930_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*
# CD Build
- stage: global-tag
if: branch = v9.3.0.x AND type != pull_request OR tag =~ ^release-candidate*
name: "Generate Global Tag"
os: linux
script: bash -e travis-build-scripts/global-tag.sh
- stage: build
if: branch = v9.3.0.x OR tag =~ ^release-candidate*
name: "Multi-Arch AMD64 build" name: "Multi-Arch AMD64 build"
os: linux os: linux
env: env:
- BUILD_ALL=true - BUILD_ALL=true
- MQ_ARCHIVE_REPOSITORY=$MQ_920_ARCHIVE_REPOSITORY_AMD64 - MQ_ARCHIVE_REPOSITORY=$MQ_930_ARCHIVE_REPOSITORY_AMD64
- MQ_ARCHIVE_REPOSITORY_DEV=$MQ_920_ARCHIVE_REPOSITORY_DEV_AMD64 - MQ_ARCHIVE_REPOSITORY_DEV=$MQ_930_ARCHIVE_REPOSITORY_DEV_AMD64
script: bash -e travis-build-scripts/run.sh
- stage: build
if: branch = v9.3.0.x OR tag =~ ^release-candidate*
name: "Multi-Arch S390X build"
os: linux-s390
env:
- BUILD_ALL=true
- TEST_OPTS_DOCKER="-run TestGoldenPathWithMetrics"
- MQ_ARCHIVE_REPOSITORY=$MQ_930_ARCHIVE_REPOSITORY_S390X
- MQ_ARCHIVE_REPOSITORY_DEV=$MQ_930_ARCHIVE_REPOSITORY_DEV_S390X
script: bash -e travis-build-scripts/run.sh
- stage: build
if: branch = v9.3.0.x 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_930_ARCHIVE_REPOSITORY_PPC64LE
- MQ_ARCHIVE_REPOSITORY_DEV=$MQ_930_ARCHIVE_REPOSITORY_DEV_PPC64LE
script: bash -e travis-build-scripts/run.sh
- stage: push-manifest
if: branch = v9.3.0.x 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 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
# - if: branch = private-master OR tag =~ ^release-candidate*
# name: "Multi-Arch S390X build"
# os: linux-s390
# env:
# - BUILD_ALL=true
# - TEST_OPTS_DOCKER="-run TestGoldenPathWithMetrics"
# # - MQ_ARCHIVE_REPOSITORY=$MQ_920_ARCHIVE_REPOSITORY_S390X
# - MQ_ARCHIVE_REPOSITORY_DEV=$MQ_920_ARCHIVE_REPOSITORY_DEV_S390X
# script: bash -e travis-build-scripts/run.sh
before_install: before_install:
- make install-build-deps - make install-build-deps

6
.whitesource Normal file
View File

@@ -0,0 +1,6 @@
{
"settingsInheritedFrom": "whitesource-config/whitesource-config@master",
"scanSettings": {
"baseBranches": ["private-master", "v9.2.0.x-eus", "v9.2.5"]
}
}

View File

@@ -1,5 +1,56 @@
# Change log # Change log
## 9.3.0.5-LTS (2023-04)
* Updated to MQ version 9.3.0.5
## 9.3.0.4-LTS (2023-02)
* Updated to MQ version 9.3.0.4
## 9.3.0.3-LTS (2023-01)
* Updated to MQ version 9.3.0.3
## 9.3.0.1-LTS (2022-09)
* Updated to MQ version 9.3.0.1
## 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)
* Added support for MQ Long Term Support (production licensed only) in the mq-container
## 9.2.0.0 (2020-07-23) ## 9.2.0.0 (2020-07-23)
* Updated to [MQ version 9.2.0.0](https://www.ibm.com/support/knowledgecenter/SSFKSJ_9.2.0/com.ibm.mq.pro.doc/q113110_.htm) * Updated to [MQ version 9.2.0.0](https://www.ibm.com/support/knowledgecenter/SSFKSJ_9.2.0/com.ibm.mq.pro.doc/q113110_.htm)
@@ -15,7 +66,7 @@
* Updated to MQ version 9.1.4.0 * Updated to MQ version 9.1.4.0
* Updated to use UBI8 as base image * Updated to use UBI8 as base image
* Added required security settings to self signed certificates to align with macOS Catalina requirements * Added required security settings to self signed certificates to align with macOS Catalina requirements
## 9.1.3.0 (2019-07-19) ## 9.1.3.0 (2019-07-19)
* Updated to MQ version 9.1.3.0 * Updated to MQ version 9.1.3.0
@@ -36,7 +87,7 @@
* Security fixes * Security fixes
* Web console added to production image * Web console added to production image
* Container built on RedHat host * Container built on RedHat host
## 9.1.2.0 (2019-03-21) ## 9.1.2.0 (2019-03-21)
* Updated to MQ version 9.1.2.0 * Updated to MQ version 9.1.2.0

View File

@@ -1,4 +1,4 @@
# © Copyright IBM Corporation 2015, 2020 # © Copyright IBM Corporation 2015, 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.
@@ -12,69 +12,73 @@
# 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.
ARG BASE_IMAGE=registry.redhat.io/ubi8/ubi-minimal ARG BASE_IMAGE=registry.access.redhat.com/ubi8/ubi-minimal
ARG BASE_TAG=8.2-301.1593113563 ARG BASE_TAG=8.8-860
ARG BUILDER_IMAGE=registry.access.redhat.com/ubi8/go-toolset
ARG BUILDER_TAG=1.19.9-2
ARG GO_WORKDIR=/opt/app-root/src/go/src/github.com/ibm-messaging/mq-container
ARG MQ_URL="https://public.dhe.ibm.com/ibmdl/export/pub/software/websphere/messaging/mqadv/9.3.0.5-IBM-MQ-Advanced-for-Developers-Non-Install-LinuxX64.tar.gz"
############################################################################### ###############################################################################
# Build stage to build Go code # Build stage to build Go code
############################################################################### ###############################################################################
FROM registry.redhat.io/ubi8/go-toolset:1.13.4-22 as builder FROM $BUILDER_IMAGE:$BUILDER_TAG as builder
# FROM docker.io/centos/go-toolset-7-centos7 as builder
# The URL to download the MQ installer from in tar.gz format # The URL to download the MQ installer from in tar.gz format
# This assumes an archive containing the MQ RPM install packages # This assumes an archive containing the MQ Non-Install packages
ARG MQ_URL="https://public.dhe.ibm.com/ibmdl/export/pub/software/websphere/messaging/mqadv/mqadv_dev912_linux_x86-64.tar.gz" ARG MQ_URL
ARG IMAGE_REVISION="Not specified" ARG IMAGE_REVISION="Not specified"
ARG IMAGE_SOURCE="Not specified" ARG IMAGE_SOURCE="Not specified"
ARG IMAGE_TAG="Not specified" ARG IMAGE_TAG="Not specified"
ARG GO_WORKDIR
USER 0 USER 0
COPY install-mq.sh /usr/local/bin/ WORKDIR /opt/mqm
RUN mkdir /opt/mqm \ # Download and extract MQ files, to get the MQ client needed to compile.
&& chmod a+x /usr/local/bin/install-mq.sh \ # Only extract certain MQ files to make the build quicker
&& sleep 1 \ RUN curl --fail --location $MQ_URL | tar --extract --gunzip \
&& INSTALL_SDK=1 install-mq.sh \
&& chown -R 1001:root /opt/mqm/* && chown -R 1001:root /opt/mqm/*
WORKDIR /opt/app-root/src/go/src/github.com/ibm-messaging/mq-container/ WORKDIR $GO_WORKDIR/
COPY go.mod go.sum ./
COPY cmd/ ./cmd COPY cmd/ ./cmd
COPY internal/ ./internal COPY internal/ ./internal
COPY pkg/ ./pkg COPY pkg/ ./pkg
COPY vendor/ ./vendor COPY vendor/ ./vendor
ENV PATH="${PATH}:/opt/rh/go-toolset-1.11/root/usr/bin" \ ENV CGO_CFLAGS="-I/opt/mqm/inc/" \
CGO_CFLAGS="-I/opt/mqm/inc/" \ CGO_LDFLAGS_ALLOW="-Wl,-rpath.*" \
CGO_LDFLAGS_ALLOW="-Wl,-rpath.*" PATH="${PATH}:/opt/mqm/bin"
RUN go build -ldflags "-X \"main.ImageCreated=$(date --iso-8601=seconds)\" -X \"main.ImageRevision=$IMAGE_REVISION\" -X \"main.ImageSource=$IMAGE_SOURCE\" -X \"main.ImageTag=$IMAGE_TAG\"" ./cmd/runmqserver/ 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/ && go build ./cmd/chkmqready/ \
RUN go build ./cmd/chkmqhealthy/ && go build ./cmd/chkmqhealthy/ \
RUN go build ./cmd/runmqdevserver/ && go build ./cmd/chkmqstarted/ \
RUN go build -buildmode=c-shared -o amqpasdev.so ./internal/qmgrauth/pas.go && go build ./cmd/runmqdevserver/ \
RUN go test -v ./cmd/runmqdevserver/... && go test -v ./cmd/runmqdevserver/... \
RUN go test -v ./cmd/runmqserver/ && go test -v ./cmd/runmqserver/ \
RUN go test -v ./cmd/chkmqready/ && go test -v ./cmd/chkmqready/ \
RUN go test -v ./cmd/chkmqhealthy/ && go test -v ./cmd/chkmqhealthy/ \
RUN go test -v ./pkg/... && go test -v ./cmd/chkmqstarted/ \
RUN go test -v ./internal/... && go test -v ./pkg/... \
RUN go vet ./cmd/... ./internal/... && go test -v ./internal/... \
&& go vet ./cmd/... ./internal/...
############################################################################### ###############################################################################
# Main build stage, to build MQ image # Main build stage, to build MQ image
############################################################################### ###############################################################################
FROM $BASE_IMAGE:$BASE_TAG AS mq-server FROM $BASE_IMAGE:$BASE_TAG AS mq-server
# The MQ packages to install - see install-mq.sh for default value # The MQ packages to install - see install-mq.sh for default value
ARG MQ_URL="https://public.dhe.ibm.com/ibmdl/export/pub/software/websphere/messaging/mqadv/mqadv_dev914_linux_x86-64.tar.gz" ARG MQ_URL
ARG MQ_PACKAGES="MQSeriesRuntime-*.rpm MQSeriesServer-*.rpm MQSeriesJava*.rpm MQSeriesJRE*.rpm MQSeriesGSKit*.rpm MQSeriesMsg*.rpm MQSeriesSamples*.rpm MQSeriesWeb*.rpm MQSeriesAMS-*.rpm"
#ARG MQ_PACKAGES="ibmmq-server ibmmq-java ibmmq-jre ibmmq-gskit ibmmq-msg-.* ibmmq-samples ibmmq-web ibmmq-ams"
ARG BASE_IMAGE ARG BASE_IMAGE
ARG BASE_TAG ARG BASE_TAG
LABEL summary="IBM MQ Advanced Server" ARG GO_WORKDIR
LABEL description="Simplify, accelerate and facilitate the reliable exchange of data with a security-rich messaging solution — trusted by the worlds most successful enterprises" LABEL summary="IBM MQ Advanced Server" \
LABEL vendor="IBM" description="Simplify, accelerate and facilitate the reliable exchange of data with a security-rich messaging solution — trusted by the worlds most successful enterprises" \
LABEL maintainer="IBM" vendor="IBM" \
LABEL distribution-scope="private" maintainer="IBM" \
LABEL authoritative-source-url="https://www.ibm.com/software/passportadvantage/" distribution-scope="private" \
LABEL url="https://www.ibm.com/products/mq/advanced" authoritative-source-url="https://www.ibm.com/software/passportadvantage/" \
LABEL io.openshift.tags="mq messaging" url="https://www.ibm.com/products/mq/advanced" \
LABEL io.k8s.display-name="IBM MQ Advanced Server" io.openshift.tags="mq messaging" \
LABEL io.k8s.description="Simplify, accelerate and facilitate the reliable exchange of data with a security-rich messaging solution — trusted by the worlds most successful enterprises" io.k8s.display-name="IBM MQ Advanced Server" \
LABEL base-image=$BASE_IMAGE io.k8s.description="Simplify, accelerate and facilitate the reliable exchange of data with a security-rich messaging solution — trusted by the worlds most successful enterprises" \
LABEL base-image-release=$BASE_TAG base-image=$BASE_IMAGE \
base-image-release=$BASE_TAG
COPY install-mq.sh /usr/local/bin/ COPY install-mq.sh /usr/local/bin/
COPY install-mq-server-prereqs.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. # Install MQ. To avoid a "text file busy" error here, we sleep before installing.
@@ -86,18 +90,16 @@ RUN env \
&& install-mq.sh \ && install-mq.sh \
&& /opt/mqm/bin/security/amqpamcf \ && /opt/mqm/bin/security/amqpamcf \
&& chown -R 1001:root /opt/mqm/* && chown -R 1001:root /opt/mqm/*
# Create a directory for runtime data from runmqserver COPY --from=builder $GO_WORKDIR/runmqserver /usr/local/bin/
RUN mkdir -p /run/runmqserver \ COPY --from=builder $GO_WORKDIR/chkmq* /usr/local/bin/
&& chown 1001:root /run/runmqserver
COPY --from=builder /opt/app-root/src/go/src/github.com/ibm-messaging/mq-container/runmqserver /usr/local/bin/
COPY --from=builder /opt/app-root/src/go/src/github.com/ibm-messaging/mq-container/chkmq* /usr/local/bin/
COPY NOTICES.txt /opt/mqm/licenses/notices-container.txt COPY 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 XML files
COPY web /etc/mqm/web COPY web /etc/mqm/web
COPY etc/mqm/*.tpl /etc/mqm/ COPY etc/mqm/*.tpl /etc/mqm/
RUN chmod ug+x /usr/local/bin/runmqserver \ RUN chmod ug+x /usr/local/bin/runmqserver \
&& chown 1001:root /usr/local/bin/*mq* \ && chown 1001:root /usr/local/bin/*mq* \
&& chmod ug+xs /usr/local/bin/chkmq* \ && chmod ug+x /usr/local/bin/chkmq* \
&& chown -R 1001:root /etc/mqm/* \ && chown -R 1001:root /etc/mqm/* \
&& install --directory --mode 2775 --owner 1001 --group root /run/runmqserver \ && install --directory --mode 2775 --owner 1001 --group root /run/runmqserver \
&& touch /run/termination-log \ && touch /run/termination-log \
@@ -114,38 +116,48 @@ USER 1001
ENV MQ_CONNAUTH_USE_HTP=false ENV MQ_CONNAUTH_USE_HTP=false
ENTRYPOINT ["runmqserver"] 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
# The URL to download the MQ installer from in tar.gz format
# This assumes an archive containing the MQ Non-Install packages
ARG MQ_URL
USER 0
# Install the Apache Portable Runtime code (used for htpasswd hash checking)
RUN yum --assumeyes --disableplugin=subscription-manager install apr-devel apr-util-openssl apr-util-devel
COPY authservice/ /opt/app-root/src/authservice/
WORKDIR /opt/app-root/src/authservice/mqhtpass
RUN make all
############################################################################### ###############################################################################
# Add default developer config # Add default developer config
############################################################################### ###############################################################################
FROM mq-server AS mq-dev-server FROM mq-server AS mq-dev-server
ARG BASE_IMAGE ARG BASE_IMAGE
ARG BASE_TAG ARG BASE_TAG
# Enable MQ developer default configuration ARG GO_WORKDIR
ENV MQ_DEV=true LABEL summary="IBM MQ Advanced for Developers Server" \
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 worlds most successful enterprises" \
LABEL description="Simplify, accelerate and facilitate the reliable exchange of data with a security-rich messaging solution — trusted by the worlds most successful enterprises" vendor="IBM" \
LABEL vendor="IBM" distribution-scope="private" \
LABEL distribution-scope="private" authoritative-source-url="https://www.ibm.com/software/passportadvantage/" \
LABEL authoritative-source-url="https://www.ibm.com/software/passportadvantage/" url="https://www.ibm.com/products/mq/advanced" \
LABEL url="https://www.ibm.com/products/mq/advanced" io.openshift.tags="mq messaging" \
LABEL io.openshift.tags="mq messaging" io.k8s.display-name="IBM MQ Advanced for Developers Server" \
LABEL 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 worlds most successful enterprises" \
LABEL io.k8s.description="Simplify, accelerate and facilitate the reliable exchange of data with a security-rich messaging solution — trusted by the worlds most successful enterprises" base-image=$BASE_IMAGE \
LABEL base-image=$BASE_IMAGE base-image-release=$BASE_TAG
LABEL base-image-release=$BASE_TAG
USER 0 USER 0
COPY --from=builder /opt/app-root/src/go/src/github.com/ibm-messaging/mq-container/amqpas* /opt/mqm/lib64/ COPY --from=cbuilder /opt/app-root/src/authservice/mqhtpass/build/mqhtpass.so /opt/mqm/lib64/
COPY etc/mqm/*.ini /etc/mqm/ COPY etc/mqm/*.ini /etc/mqm/
COPY etc/mqm/mq.htpasswd /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/ COPY incubating/mqadvanced-server-dev/install-extra-packages.sh /usr/local/bin/
RUN chmod u+x /usr/local/bin/install-extra-packages.sh \ RUN chmod u+x /usr/local/bin/install-extra-packages.sh \
&& sleep 1 \ && sleep 1 \
&& install-extra-packages.sh && install-extra-packages.sh
# Create a directory for runtime data from runmqserver COPY --from=builder $GO_WORKDIR/runmqdevserver /usr/local/bin/
RUN mkdir -p /run/runmqdevserver \
&& chown 1001:root /run/runmqdevserver
COPY --from=builder /opt/app-root/src/go/src/github.com/ibm-messaging/mq-container/runmqdevserver /usr/local/bin/
# Copy template files # Copy template files
COPY incubating/mqadvanced-server-dev/*.tpl /etc/mqm/ COPY incubating/mqadvanced-server-dev/*.tpl /etc/mqm/
# Copy web XML files for default developer configuration # Copy web XML files for default developer configuration
@@ -153,10 +165,13 @@ COPY incubating/mqadvanced-server-dev/web /etc/mqm/web
RUN chown -R 1001:root /etc/mqm/* \ RUN chown -R 1001:root /etc/mqm/* \
&& chmod -R g+w /etc/mqm/web \ && chmod -R g+w /etc/mqm/web \
&& chmod +x /usr/local/bin/runmq* \ && chmod +x /usr/local/bin/runmq* \
&& chmod 0660 /etc/mqm/mq.htpasswd \
&& install --directory --mode 2775 --owner 1001 --group root /run/runmqdevserver && install --directory --mode 2775 --owner 1001 --group root /run/runmqdevserver
ENV MQ_ENABLE_EMBEDDED_WEB_SERVER=1 MQ_GENERATE_CERTIFICATE_HOSTNAME=localhost ENV MQ_DEV=true \
ENV LD_LIBRARY_PATH=/opt/mqm/lib64 MQ_ENABLE_EMBEDDED_WEB_SERVER=1 \
ENV MQS_PERMIT_UNKNOWN_ID=true MQ_GENERATE_CERTIFICATE_HOSTNAME=localhost \
ENV MQ_CONNAUTH_USE_HTP=true LD_LIBRARY_PATH=/opt/mqm/lib64 \
MQ_CONNAUTH_USE_HTP=true \
MQS_PERMIT_UNKNOWN_ID=true
USER 1001 USER 1001
ENTRYPOINT ["runmqdevserver"] ENTRYPOINT ["runmqdevserver"]

366
Makefile
View File

@@ -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.
@@ -16,10 +16,12 @@
# Conditional variables - you can override the values of these variables from # Conditional variables - you can override the values of these variables from
# the command line # the command line
############################################################################### ###############################################################################
include config.env
include source-branch.env
# 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_VERSION is the fully qualified MQ version number to build
MQ_VERSION ?= 9.2.0.0
# 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)
MQ_ARCHIVE_REPOSITORY ?= MQ_ARCHIVE_REPOSITORY ?=
# MQ_ARCHIVE_REPOSITORY_DEV is a remote repository from which to pull the MQ_ARCHIVE_DEV (if required) # MQ_ARCHIVE_REPOSITORY_DEV is a remote repository from which to pull the MQ_ARCHIVE_DEV (if required)
@@ -38,12 +40,16 @@ MQ_ARCHIVE_DEV ?= $(MQ_VERSION)-IBM-MQ-Advanced-for-Developers-Non-Install-$(MQ_
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 Docker tests
TEST_OPTS_DOCKER ?= TEST_OPTS_DOCKER ?=
# Timeout for the Docker tests
TEST_TIMEOUT_DOCKER ?= 30m
# 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
MQ_IMAGE_DEVSERVER ?=ibm-mqadvanced-server-dev MQ_IMAGE_DEVSERVER ?=ibm-mqadvanced-server-dev
# MQ_MANIFEST_TAG is the tag to use for fat-manifest
MQ_MANIFEST_TAG ?= $(MQ_VERSION)$(RELEASE_TAG)$(LTS_TAG)$(MQ_MANIFEST_TAG_SUFFIX)
# MQ_TAG is the tag of the built MQ Advanced image & MQ Advanced for Developers image # MQ_TAG is the tag of the built MQ Advanced image & MQ Advanced for Developers image
MQ_TAG ?=$(MQ_VERSION)-$(ARCH) MQ_TAG ?= $(MQ_MANIFEST_TAG)-$(ARCH)
# COMMAND is the container command to run. "podman" or "docker" # COMMAND is the container command to run. "podman" or "docker"
COMMAND ?=$(shell type -p podman 2>&1 >/dev/null && echo podman || echo docker) COMMAND ?=$(shell type -p podman 2>&1 >/dev/null && echo podman || echo docker)
# MQ_DELIVERY_REGISTRY_HOSTNAME is a remote registry to push the MQ Image to (if required) # MQ_DELIVERY_REGISTRY_HOSTNAME is a remote registry to push the MQ Image to (if required)
@@ -54,24 +60,28 @@ 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 is the platform architecture (e.g. amd64, ppc64le or s390x)
ARCH ?= $(if $(findstring x86_64,$(shell uname -m)),amd64,$(shell uname -m)) ARCH ?= $(if $(findstring x86_64,$(shell uname -m)),amd64,$(shell uname -m))
# LTS is a boolean value to enable/disable LTS container build
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)))
@@ -104,19 +114,84 @@ else ifeq "$(ARCH)" "s390x"
MQ_ARCHIVE_ARCH=S390X MQ_ARCHIVE_ARCH=S390X
endif 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
# LTS_TAG is the tag modifier for an LTS container build
LTS_TAG=
ifeq "$(LTS)" "true"
ifneq "$(LTS_TAG_OVERRIDE)" "$(EMPTY)"
LTS_TAG=$(LTS_TAG_OVERRIDE)
else
LTS_TAG=-lts
endif
MQ_ARCHIVE:=$(MQ_VERSION)-IBM-MQ-Advanced-Non-Install-Linux$(MQ_ARCHIVE_ARCH).tar.gz
MQ_DELIVERY_REGISTRY_NAMESPACE:=$(MQ_DELIVERY_REGISTRY_NAMESPACE)$(LTS_TAG)
endif
ifneq (,$(findstring release-candidate,$(TRAVIS_TAG)))
MQ_DELIVERY_REGISTRY_NAMESPACE=release-candidates
endif
ifneq "$(MQ_DELIVERY_REGISTRY_NAMESPACE)" "$(EMPTY)" ifneq "$(MQ_DELIVERY_REGISTRY_NAMESPACE)" "$(EMPTY)"
MQ_DELIVERY_REGISTRY_FULL_PATH=$(MQ_DELIVERY_REGISTRY_HOSTNAME)/$(MQ_DELIVERY_REGISTRY_NAMESPACE) MQ_DELIVERY_REGISTRY_FULL_PATH=$(MQ_DELIVERY_REGISTRY_HOSTNAME)/$(MQ_DELIVERY_REGISTRY_NAMESPACE)
else else
MQ_DELIVERY_REGISTRY_FULL_PATH=$(MQ_DELIVERY_REGISTRY_HOSTNAME) MQ_DELIVERY_REGISTRY_FULL_PATH=$(MQ_DELIVERY_REGISTRY_HOSTNAME)
endif endif
# image tagging
ifneq "$(RELEASE)" "$(EMPTY)" ifneq "$(RELEASE)" "$(EMPTY)"
MQ_TAG=$(MQ_VERSION)-$(RELEASE)-$(ARCH) EXTRA_LABELS_RELEASE=--label "release=$(RELEASE)"
EXTRA_LABELS=--label release=$(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)"
TIMESTAMPFLAT=$(shell date "+%Y%m%d%H%M%S")
endif
ifeq "$(GIT_COMMIT)" "$(EMPTY)"
GIT_COMMIT=$(shell git rev-parse --short HEAD)
endif
ifeq ($(shell [ ! -z $(TRAVIS) ] && [ "$(TRAVIS_PULL_REQUEST)" = "false" ] && [ "$(TRAVIS_BRANCH)" = "$(MAIN_BRANCH)" ] && echo true), true)
MQ_MANIFEST_TAG_SUFFIX=.$(TIMESTAMPFLAT).$(GIT_COMMIT)
endif
PATH_TO_MQ_TAG_CACHE=$(TRAVIS_BUILD_DIR)/.tagcache
ifneq "$(TRAVIS)" "$(EMPTY)"
ifneq ("$(wildcard $(PATH_TO_MQ_TAG_CACHE))","")
include $(PATH_TO_MQ_TAG_CACHE)
endif
endif
MQ_AMD64_TAG=$(MQ_MANIFEST_TAG)-amd64
MQ_S390X_TAG?=$(MQ_MANIFEST_TAG)-s390x
MQ_PPC64LE_TAG?=$(MQ_MANIFEST_TAG)-ppc64le
# end image tagging
MQ_IMAGE_FULL_RELEASE_NAME=$(MQ_IMAGE_ADVANCEDSERVER):$(MQ_TAG) MQ_IMAGE_FULL_RELEASE_NAME=$(MQ_IMAGE_ADVANCEDSERVER):$(MQ_TAG)
MQ_IMAGE_DEV_FULL_RELEASE_NAME=$(MQ_IMAGE_DEVSERVER):$(MQ_TAG) MQ_IMAGE_DEV_FULL_RELEASE_NAME=$(MQ_IMAGE_DEVSERVER):$(MQ_TAG)
#setup variables for fat-manifests
MQ_IMAGE_DEVSERVER_MANIFEST=$(MQ_IMAGE_DEVSERVER):$(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_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_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
############################################################################### ###############################################################################
@@ -133,6 +208,9 @@ test-all: build-devjmstest test-devserver test-advancedserver
.PHONY: devserver .PHONY: devserver
devserver: build-devserver build-devjmstest test-devserver devserver: build-devserver build-devjmstest test-devserver
.PHONY: advancedserver
advancedserver: build-advancedserver test-advancedserver
# Build incubating components # Build incubating components
.PHONY: incubating .PHONY: incubating
incubating: build-explorer incubating: build-explorer
@@ -140,36 +218,63 @@ incubating: build-explorer
downloads/$(MQ_ARCHIVE_DEV): downloads/$(MQ_ARCHIVE_DEV):
$(info $(SPACER)$(shell printf $(TITLE)"Downloading IBM MQ Advanced for Developers "$(MQ_VERSION)$(END))) $(info $(SPACER)$(shell printf $(TITLE)"Downloading IBM MQ Advanced for Developers "$(MQ_VERSION)$(END)))
mkdir -p downloads mkdir -p downloads
ifneq "$(MQ_ARCHIVE_REPOSITORY_DEV)" "$(EMPTY)" ifneq "$(BUILD_RSYNC_SERVER)" "$(EMPTY)"
curl -u $(MQ_ARCHIVE_REPOSITORY_USER):$(MQ_ARCHIVE_REPOSITORY_CREDENTIAL) -X GET "$(MQ_ARCHIVE_REPOSITORY_DEV)" -o downloads/$(MQ_ARCHIVE_DEV) # Use key which is not stored in the repository to fetch the files from the fileserver
curl --fail --location $(BUILD_RSYNC_ENCRYPTED_KEY_URL) --output ./host.key.gpg
@echo $(BUILD_RSYNC_ENCRYPTION_PASSWORD)|gpg --batch --passphrase-fd 0 ./host.key.gpg
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)
-@rm host.key.gpg host.key
else else
curl -L https://public.dhe.ibm.com/ibmdl/export/pub/software/websphere/messaging/mqadv/$(MQ_ARCHIVE_DEV) -o downloads/$(MQ_ARCHIVE_DEV) ifneq "$(MQ_ARCHIVE_REPOSITORY_DEV)" "$(EMPTY)"
curl --fail --user $(MQ_ARCHIVE_REPOSITORY_USER):$(MQ_ARCHIVE_REPOSITORY_CREDENTIAL) --request GET "$(MQ_ARCHIVE_REPOSITORY_DEV)" --output downloads/$(MQ_ARCHIVE_DEV)
else
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
downloads/$(MQ_ARCHIVE): downloads/$(MQ_ARCHIVE):
$(info $(SPACER)$(shell printf $(TITLE)"Downloading IBM MQ Advanced "$(MQ_VERSION)$(END))) $(info $(SPACER)$(shell printf $(TITLE)"Downloading IBM MQ Advanced "$(MQ_VERSION)$(END)))
mkdir -p downloads mkdir -p downloads
ifneq "$(BUILD_RSYNC_SERVER)" "$(EMPTY)"
# Use key which is not stored in the repository to fetch the files from the fileserver
-@rm host.key.gpg host.key
curl --fail --location $(BUILD_RSYNC_ENCRYPTED_KEY_URL) --output ./host.key.gpg
@echo $(BUILD_RSYNC_ENCRYPTION_PASSWORD)|gpg --batch --passphrase-fd 0 ./host.key.gpg
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)
-@rm host.key.gpg host.key
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
.PHONY: downloads .PHONY: downloads
downloads: downloads/$(MQ_ARCHIVE_DEV) downloads/$(MQ_SDK_ARCHIVE) downloads: downloads/$(MQ_ARCHIVE_DEV) downloads/$(MQ_SDK_ARCHIVE)
.PHONY: cache-mq-tag
cache-mq-tag:
@printf "MQ_MANIFEST_TAG=$(MQ_MANIFEST_TAG)\n" | tee $(PATH_TO_MQ_TAG_CACHE)
###############################################################################
# Test targets
###############################################################################
# Vendor Go dependencies for the Docker tests # Vendor Go dependencies for the Docker tests
test/docker/vendor: test/docker/vendor:
cd test/docker && dep ensure -vendor-only cd test/docker && 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/docker/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) $(TEST_OPTS_DOCKER) cd test/docker && TEST_IMAGE=$(MQ_IMAGE_ADVANCEDSERVER):$(MQ_TAG) EXPECTED_LICENSE=Production DOCKER_API_VERSION=$(DOCKER_API_VERSION) go test -parallel $(NUM_CPU) -timeout $(TEST_TIMEOUT_DOCKER) $(TEST_OPTS_DOCKER)
.PHONY: build-devjmstest .PHONY: build-devjmstest
build-devjmstest: build-devjmstest:
@@ -178,9 +283,9 @@ build-devjmstest:
.PHONY: test-devserver .PHONY: test-devserver
test-devserver: test/docker/vendor test-devserver: test/docker/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) -tags mqdev $(TEST_OPTS_DOCKER) cd test/docker && TEST_IMAGE=$(MQ_IMAGE_DEVSERVER):$(MQ_TAG) EXPECTED_LICENSE=Developer DEV_JMS_IMAGE=$(DEV_JMS_IMAGE) IBMJRE=true DOCKER_API_VERSION=$(DOCKER_API_VERSION) go test -parallel $(NUM_CPU) -timeout $(TEST_TIMEOUT_DOCKER) -tags mqdev $(TEST_OPTS_DOCKER)
.PHONY: coverage .PHONY: coverage
coverage: coverage:
@@ -188,7 +293,7 @@ coverage:
.PHONY: test-advancedserver-cover .PHONY: test-advancedserver-cover
test-advancedserver-cover: test/docker/vendor coverage test-advancedserver-cover: test/docker/vendor coverage
$(info $(SPACER)$(shell printf $(TITLE)"Test $(MQ_IMAGE_ADVANCEDSERVER):$(MQ_TAG) with code coverage on $(shell docker --version)"$(END))) $(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/{}
@@ -200,7 +305,7 @@ test-advancedserver-cover: test/docker/vendor coverage
rm -f ./test/docker/coverage/*.cov rm -f ./test/docker/coverage/*.cov
rm -f ./coverage/docker.* rm -f ./coverage/docker.*
mkdir -p ./test/docker/coverage/ mkdir -p ./test/docker/coverage/
cd test/docker && TEST_IMAGE=$(MQ_IMAGE_ADVANCEDSERVER):$(MQ_TAG)-cover TEST_COVER=true go test $(TEST_OPTS_DOCKER) cd test/docker && TEST_IMAGE=$(MQ_IMAGE_ADVANCEDSERVER):$(MQ_TAG)-cover TEST_COVER=true DOCKER_API_VERSION=$(DOCKER_API_VERSION) go test $(TEST_OPTS_DOCKER)
echo 'mode: count' > ./coverage/docker.cov echo 'mode: count' > ./coverage/docker.cov
tail -q -n +2 ./test/docker/coverage/*.cov >> ./coverage/docker.cov tail -q -n +2 ./test/docker/coverage/*.cov >> ./coverage/docker.cov
go tool cover -html=./coverage/docker.cov -o ./coverage/docker.html go tool cover -html=./coverage/docker.cov -o ./coverage/docker.html
@@ -209,16 +314,17 @@ test-advancedserver-cover: test/docker/vendor coverage
tail -q -n +2 ./coverage/unit.cov ./coverage/docker.cov >> ./coverage/combined.cov tail -q -n +2 ./coverage/unit.cov ./coverage/docker.cov >> ./coverage/combined.cov
go tool cover -html=./coverage/combined.cov -o ./coverage/combined.html 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 ###############################################################################
define build-mq # Build functions
$(if $(findstring docker,$(COMMAND)), @docker network create build,) ###############################################################################
$(if $(findstring docker,$(COMMAND)), @docker run --rm --name $(BUILD_SERVER_CONTAINER) --network build --network-alias build --volume $(DOWNLOADS_DIR):/usr/share/nginx/html:ro --detach docker.io/nginx:alpine,)
$(eval EXTRA_ARGS=$(if $(findstring docker,$(COMMAND)), --network build --build-arg MQ_URL=http://build:80/$4, --volume $(DOWNLOADS_DIR):/var/downloads --build-arg MQ_URL=file:///var/downloads/$4)) # Command to build the image
# Build the new image # Args: imageName, imageTag, dockerfile, extraArgs, dockerfileTarget
define build-mq-command
$(COMMAND) build \ $(COMMAND) build \
--tag $1:$2 \ --tag $1:$2 \
--file $3 \ --file $3 \
$(EXTRA_ARGS) \ $4 \
--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" \
@@ -226,36 +332,81 @@ define build-mq
--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) \
$(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 }}") # Build using a separate container to host the MQ download files.
DOCKER_CLIENT_VERSION=$(shell docker version --format "{{ .Client.Version }}") # To minimize the layers in the resulting image, the download files can't be part of the build context.
PODMAN_VERSION=$(shell podman version --format "{{ .Version }}") # The "docker build" command (and "podman build" on macOS) don't allow you to mount a directory into the build, so a
.PHONY: command-version # separate container is used to host a web server.
command-version: # Note that for Podman, this means that you need to be using the "rootful" mode, because the rootless mode doesn't allow
# If we're using Docker, then check it's recent enough to support multi-stage builds # much control of networking, so the containers can't talk to each other.
ifneq (,$(findstring docker,$(COMMAND))) define build-mq-using-web-server
@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) $(COMMAND) network create $(BUILD_SERVER_NETWORK)
@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) $(COMMAND) run \
endif --rm \
ifneq (,$(findstring podman,$(COMMAND))) --name $(BUILD_SERVER_CONTAINER) \
@test "$(word 1,$(subst ., ,$(PODMAN_VERSION)))" -ge "1" || (echo "Error: Podman version 1.0 or greater is required" && exit 1) --network $(BUILD_SERVER_NETWORK) \
--volume $(DOWNLOADS_DIR):/opt/app-root/src$(VOLUME_MOUNT_OPTIONS) \
--detach \
registry.access.redhat.com/ubi8/nginx-120 nginx -g "daemon off;" || ($(COMMAND) network rm $(BUILD_SERVER_NETWORK) && exit 1)
BUILD_SERVER_IP=$$($(COMMAND) inspect -f '{{ .NetworkSettings.Networks.$(BUILD_SERVER_NETWORK).IPAddress }}' $(BUILD_SERVER_CONTAINER)); \
DOCKER_BUILDKIT=0 $(call build-mq-command,$1,$2,$3,--network build --build-arg MQ_URL=http://$$BUILD_SERVER_IP:8080/$4,$5) || ($(COMMAND) rm -f $(BUILD_SERVER_CONTAINER) && $(COMMAND) network rm $(BUILD_SERVER_NETWORK) && exit 1)
$(COMMAND) rm -f $(BUILD_SERVER_CONTAINER)
$(COMMAND) network rm $(BUILD_SERVER_NETWORK)
endef
# When building with Docker, always use the web server build because you can't use bind-mounted volumes.
# Args: imageName, imageTag, dockerfile, mqArchive, dockerfileTarget
define build-mq-docker
$(call build-mq-using-web-server,$1,$2,$3,$4,$5)
endef
# 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 endif
# When building with Podman on macOS (Darwin), use the web server build because you can't use bind-mounted volumes with `podman build` on macOS
# Args: imageName, imageTag, dockerfile, mqArchive, dockerfileTarget
define build-mq-podman-Darwin
$(call build-mq-using-web-server,$1,$2,$3,$4,$5)
endef
# When building with Podman on Linux, just pass the downloads directory as a volume
# Args: imageName, imageTag, dockerfile, mqArchive, dockerfileTarget
define build-mq-podman-Linux
$(call build-mq-command,$1,$2,$3,--volume $(DOWNLOADS_DIR):/var/downloads$(VOLUME_MOUNT_OPTIONS) --build-arg MQ_URL=file:///var/downloads/$4,$5)
endef
# When building with Podman, just pass the downloads directory as a volume
# Args: imageName, imageTag, dockerfile, mqArchive, dockerfileTarget
define build-mq-podman
$(call build-mq-podman-$(shell uname -s),$1,$2,$3,$4,$5)
endef
# Build an MQ image. The commands used are slightly different between Docker and Podman
# Args: imageName, imageTag, dockerfile, mqArchive, dockerfileTarget
define build-mq
$(call build-mq-$(COMMAND),$1,$2,$3,$4,$5)
endef
###############################################################################
# Build targets
###############################################################################
.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)
@@ -263,29 +414,26 @@ 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)))
@@ -296,7 +444,6 @@ log-build-vars:
@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
@@ -306,16 +453,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)
@@ -323,6 +476,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)
@@ -342,6 +498,38 @@ pull-devserver:
$(COMMAND) pull $(MQ_DELIVERY_REGISTRY_FULL_PATH)/$(MQ_IMAGE_DEV_FULL_RELEASE_NAME) $(COMMAND) pull $(MQ_DELIVERY_REGISTRY_FULL_PATH)/$(MQ_IMAGE_DEV_FULL_RELEASE_NAME)
$(COMMAND) tag $(MQ_DELIVERY_REGISTRY_FULL_PATH)/$(MQ_IMAGE_DEV_FULL_RELEASE_NAME) $(MQ_IMAGE_DEVSERVER)\:$(MQ_TAG) $(COMMAND) tag $(MQ_DELIVERY_REGISTRY_FULL_PATH)/$(MQ_IMAGE_DEV_FULL_RELEASE_NAME) $(MQ_IMAGE_DEVSERVER)\:$(MQ_TAG)
.PHONY: push-manifest
push-manifest: build-skopeo-container
$(info $(SPACER)$(shell printf $(TITLE)"** Determining the image digests **"$(END)))
ifneq "$(LTS)" "true"
$(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_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_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
$(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_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_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"
$(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) $(MQ_IMAGE_DEVSERVER_PPC64LE_DIGEST)" $(END))
endif
$(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) $(MQ_IMAGE_ADVANCEDSERVER_PPC64LE_DIGEST)" $(END))
.PHONY: build-skopeo-container
build-skopeo-container:
$(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:
rm -rf ./coverage rm -rf ./coverage
@@ -379,28 +567,40 @@ 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" ;\ cat gosec_results.json | jq '{"GolangErrors": (.["Golang errors"]|length>0),"Issues":(.Issues|length>0)}' | grep 'true' >/dev/null ;\
cat gosec_results.json | grep HIGH | grep severity > /dev/null ;\ if [ $$? -eq 0 ] || [ $$gosecrc -ne 0 ]; then \
if [ $$? -eq 0 ]; then \ printf "FAILURE: Issues found running gosec - see gosec_results.json\n" ;\
printf "\nFAILURE: gosec found files containing HIGH severity issues - see results.json\n" ;\ cat "gosec_results.json" ;\
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
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
$(eval MQ_VERSION_1=$(shell echo '${MQ_VERSION}' | rev | cut -c 3- | rev))
sed -i.bak 's/IBM_MQ_.*_LINUX_X86-64_NOINST.tar.gz/IBM_MQ_${MQ_VERSION_1}_LINUX_X86-64_NOINST.tar.gz/g' docs/building.md && rm docs/building.md.bak
sed -i.bak 's/ibm-mqadvanced-server:.*-amd64/ibm-mqadvanced-server:$(MQ_VERSION)-amd64/g' docs/security.md
sed -i.bak 's/ibm-mqadvanced-server-dev.*-amd64/ibm-mqadvanced-server-dev:$(MQ_VERSION)-amd64/g' docs/security.md && rm docs/security.md.bak
sed -i.bak 's/MQ_IMAGE_ADVANCEDSERVER=ibm-mqadvanced-server:.*-amd64/MQ_IMAGE_ADVANCEDSERVER=ibm-mqadvanced-server:$(MQ_VERSION)-amd64/g' docs/testing.md && rm docs/testing.md.bak
$(eval MQ_VERSION_2=$(shell echo '${MQ_VERSION_1}' | rev | cut -c 3- | rev))
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"))
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

View File

@@ -1,5 +1,6 @@
# IBM MQ container # IBM MQ container
[![Build Status](https://travis-ci.org/ibm-messaging/mq-container.svg?branch=master)](https://travis-ci.org/ibm-messaging/mq-container) [![Build Status](https://travis-ci.org/ibm-messaging/mq-container.svg?branch=master)](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.
@@ -33,7 +34,7 @@ See the [default developer configuration docs](docs/developer-config.md) for the
### 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
@@ -44,11 +45,12 @@ For issues relating specifically to the container image or Helm chart, please us
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-APIG-CAUEQC) (International License Agreement for Non-Warranted Programs). This license may be viewed from an image using the `LICENSE=view` environment variable as described above or by following the link above.
- [IBM MQ Advanced](http://www14.software.ibm.com/cgi-bin/weblap/lap.pl?la_formnum=Z125-3301-14&li_formnum=L-APIG-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-APIG-CAUEBE) (International Program License Agreement). This license may be viewed from an image using the `LICENSE=view` environment variable as described above or by following the link above.
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, 2022

View File

@@ -0,0 +1,50 @@
# © Copyright IBM Corporation 2017, 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 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
# Flags passed to the C compiler. Need to use gnu11 to get POSIX functions needed for file locking.
CFLAGS += -std=gnu11 -fPIC -Wall -m64
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.o : $(SRC_DIR)/log.c $(SRC_DIR)/log.h
mkdir -p ${dir $@}
gcc $(CFLAGS) -c $(SRC_DIR)/log.c -o $@
$(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 $@

View 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;
}

View 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

View 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");
}

View File

@@ -0,0 +1,2 @@
fred:$2y$05$3Fp9epsqEwWOHdyj9Ngf9.qfX34kzc9zNrdQ7kac0GmcCvQjIkAwy
barney:$2y$05$l8EoyCQ9y2PyfUzIDDfTyu7SSaJEYB1TuHy07xZvN7xt/pR3SIw0a

View File

@@ -0,0 +1,3 @@
fred:$2y$05$3Fp9epsqEwWOHdyj9Ngf9.qfX34kzc9zNrdQ7kac0GmcCvQjIkAwy
barney:$2y$05$l8EoyCQ9y2PyfUzIDDfTyu7SSaJEYB1TuHy07xZvN7xt/pR3SIw0a
namewhichisfartoolongformq:$2y$05$l8EoyCQ9y2PyfUzIDDfTyu7SSaJEYB1TuHy07xZvN7xt/pR3SIw0a

View File

@@ -0,0 +1,152 @@
/*
© 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 <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 / 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, buf);
}
}

View File

@@ -0,0 +1,63 @@
/*
© 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 _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__)
#endif

View File

@@ -0,0 +1,350 @@
/*
© 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"
static char *trim(char *s);
/**
* 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"), trim(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",
trim(pIdentityContext->UserIdentifier),
trim(pApplicationContext->EffectiveUserID),
trim(pApplicationContext->ApplName),
trim(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",
trim(pIdentityContext->UserIdentifier),
trim(pApplicationContext->EffectiveUserID),
trim(pApplicationContext->ApplName),
trim(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",
trim(pIdentityContext->UserIdentifier),
trim(pApplicationContext->EffectiveUserID),
trim(pApplicationContext->ApplName),
trim(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;
}
/**
* Remove trailing spaces from a string.
*/
static char *trim(char *s)
{
int i;
for (i = strlen(s) - 1; i >= 0; i--)
{
if (s[i] == ' ')
s[i] = 0;
else
break;
}
return s;
}

View File

@@ -1,5 +1,5 @@
/* /*
© Copyright IBM Corporation 2017, 2019 © Copyright IBM Corporation 2017, 2020
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.
@@ -36,12 +36,12 @@ func queueManagerHealthy() (bool, error) {
cmd := exec.Command("dspmq", "-n", "-m", name) cmd := exec.Command("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)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return false, err return false, err
} }
fmt.Printf("%s", out) if !strings.Contains(string(out), "(RUNNING)") && !strings.Contains(string(out), "(RUNNING AS STANDBY)") && !strings.Contains(string(out), "(STARTING)") && !strings.Contains(string(out), "(REPLICA)") {
if !strings.Contains(string(out), "(RUNNING)") && !strings.Contains(string(out), "(RUNNING AS STANDBY)") && !strings.Contains(string(out), "(STARTING)") {
return false, nil return false, nil
} }
return true, nil return true, nil

View File

@@ -39,7 +39,7 @@ func main() {
} }
// Check if the queue manager has a running listener // Check if the queue manager has a running listener
if standby, _ := ready.IsRunningAsStandbyQM(name); !standby { if active, _ := ready.IsRunningAsActiveQM(name); active {
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)
@@ -49,8 +49,13 @@ func main() {
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
} }
} else { } else if standby, _ := ready.IsRunningAsStandbyQM(name); standby {
fmt.Printf("Detected queue manager running in standby mode") fmt.Printf("Detected queue manager running in standby mode")
os.Exit(10) os.Exit(10)
} else if replica, _ := ready.IsRunningAsReplicaQM(name); replica {
fmt.Printf("Detected queue manager running in replica mode")
os.Exit(20)
} else {
os.Exit(1)
} }
} }

58
cmd/chkmqstarted/main.go Normal file
View File

@@ -0,0 +1,58 @@
/*
© 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.
*/
// chkmqstarted checks that MQ has successfully started, by checking the output of the "dspmq" command
package main
import (
"fmt"
"os"
"os/exec"
"strings"
"github.com/ibm-messaging/mq-container/pkg/name"
)
func queueManagerStarted() (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.Command("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 main() {
started, err := queueManagerStarted()
if err != nil {
os.Exit(2)
}
if !started {
os.Exit(1)
}
os.Exit(0)
}

View File

@@ -1,5 +1,5 @@
/* /*
© Copyright IBM Corporation 2018, 2020 © 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.
@@ -19,7 +19,6 @@ import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os" "os"
"os/exec"
"syscall" "syscall"
"github.com/ibm-messaging/mq-container/internal/htpasswd" "github.com/ibm-messaging/mq-container/internal/htpasswd"
@@ -30,27 +29,6 @@ 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") return os.Getenv("LOG_FORMAT")
} }
@@ -99,6 +77,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)

View File

@@ -1,5 +1,5 @@
/* /*
© Copyright IBM Corporation 2017, 2020 © Copyright IBM Corporation 2017, 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.
@@ -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)
@@ -79,6 +80,8 @@ 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)
return fmt.Sprintf("%s %s\n", obj["ibm_datetime"], obj["message"]) return fmt.Sprintf("%s %s\n", obj["ibm_datetime"], obj["message"])
} }
@@ -100,6 +103,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", false, 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,14 +132,21 @@ 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 {
obj, err := processLogMessage(msg) // Check if the message is JSON
if err == nil && isQMLog && filterQMLogMessage(obj) { if len(msg) > 0 && msg[0] == '{' {
return false obj, err := processLogMessage(msg)
} if err == nil && isQMLog && filterQMLogMessage(obj) {
if err != nil { return false
log.Printf("Failed to unmarshall JSON - %v", msg) }
if err != nil {
log.Printf("Failed to unmarshall JSON in log message - %v", msg)
} else {
fmt.Println(msg)
}
} else { } else {
fmt.Println(msg) // 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
@@ -136,16 +156,22 @@ 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 {
// Parse the JSON message, and print a simplified version // Check if the message is JSON
obj, err := processLogMessage(msg) if len(msg) > 0 && msg[0] == '{' {
if err == nil && isQMLog && filterQMLogMessage(obj) { // Parse the JSON message, and print a simplified version
return false obj, err := processLogMessage(msg)
} if err == nil && isQMLog && filterQMLogMessage(obj) {
if err != nil { return false
log.Printf("Failed to unmarshall JSON - %v", err) }
if err != nil {
log.Printf("Failed to unmarshall JSON in log message - %v", err)
} else {
fmt.Printf(formatBasic(obj))
}
} else { } else {
fmt.Printf(formatBasic(obj)) // The log being mirrored isn't JSON, so just print it.
// fmt.Printf(formatSimple(obj["ibm_datetime"].(string), obj["message"].(string))) // 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

View File

@@ -1,5 +1,5 @@
/* /*
© Copyright IBM Corporation 2017, 2020 © Copyright IBM Corporation 2017, 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.
@@ -24,6 +24,7 @@ import (
"os" "os"
"sync" "sync"
"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,30 +144,55 @@ func doMain() error {
// Print out versioning information // Print out versioning information
logVersionInfo() logVersionInfo()
keyLabel, cmsKeystore, p12Truststore, err := tls.ConfigureTLSKeystores() 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) err = postInit(name, keyLabel, defaultP12Truststore)
if err != nil { if err != nil {
logTermination(err) logTermination(err)
return err return err
} }
if os.Getenv("MQ_NATIVE_HA") == "true" {
err = ha.ConfigureNativeHA(log)
if err != nil {
logTermination(err)
return err
}
}
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) newQM, err := createQueueManager(name, *devFlag)
if err != nil { if err != nil {
logTermination(err) logTermination(err)
return err return err
} }
if enableTraceCrtmqm == "true" || enableTraceCrtmqm == "1" {
err = endMQTrace()
if err != nil {
logTermination(err)
return err
}
}
var wg sync.WaitGroup var wg sync.WaitGroup
defer func() { defer func() {
log.Debug("Waiting for log mirroring to complete") log.Debug("Waiting for log mirroring to complete")
@@ -188,6 +214,23 @@ func doMain() error {
logTermination(err) logTermination(err)
return err return err
} }
if *devFlag {
_, err = mirrorHTPasswdLogs(ctx, &wg, name, newQM, mf)
if err != nil {
logTermination(err)
return err
}
}
// Recommended to use this option in conjunction with setting WLP_LOGGING_MESSAGE_FORMAT=JSON
mirrorWebLog := os.Getenv("MQ_ENABLE_EMBEDDED_WEB_SERVER_LOG")
if mirrorWebLog == "true" || mirrorWebLog == "1" {
_, err = mirrorWebServerLogs(ctx, &wg, name, newQM, mf)
if err != nil {
logTermination(err)
return err
}
}
err = updateCommandLevel() err = updateCommandLevel()
if err != nil { if err != nil {
logTermination(err) logTermination(err)

View File

@@ -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)

View File

@@ -27,6 +27,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"
) )
@@ -112,9 +113,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
@@ -203,9 +208,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")
@@ -262,7 +286,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

View File

@@ -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 {

View File

@@ -20,6 +20,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/mqversion"
) )
var ( var (
@@ -50,7 +51,7 @@ func logImageTag() {
} }
func logMQVersion() { func logMQVersion() {
mqVersion, _, err := command.Run("dspmqver", "-b", "-f", "2") mqVersion, err := mqversion.Get()
if err != nil { if err != nil {
log.Printf("Error Getting MQ version: %v", strings.TrimSuffix(string(mqVersion), "\n")) log.Printf("Error Getting MQ version: %v", strings.TrimSuffix(string(mqVersion), "\n"))
} }

View File

@@ -62,15 +62,31 @@ 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) {
// Ensure all required environment variables are set for SSO requiredEnvVars := []string{}
requiredEnvVars := []string{ _, set := os.LookupEnv("MQ_ZEN_INTERNAL_ENDPOINT")
"MQ_OIDC_CLIENT_ID", if !set {
"MQ_OIDC_CLIENT_SECRET", // Ensure all required environment variables are set for SSO
"MQ_OIDC_UNIQUE_USER_IDENTIFIER", requiredEnvVars = []string{
"MQ_OIDC_AUTHORIZATION_ENDPOINT", "MQ_OIDC_CLIENT_ID",
"MQ_OIDC_TOKEN_ENDPOINT", "MQ_OIDC_CLIENT_SECRET",
"MQ_OIDC_JWK_ENDPOINT", "MQ_OIDC_UNIQUE_USER_IDENTIFIER",
"MQ_OIDC_ISSUER_IDENTIFIER", "MQ_OIDC_AUTHORIZATION_ENDPOINT",
"MQ_OIDC_TOKEN_ENDPOINT",
"MQ_OIDC_JWK_ENDPOINT",
"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 {

6
config.env Normal file
View File

@@ -0,0 +1,6 @@
###########################################################################################################################################################
# MQ_VERSION is the fully qualified MQ version number to build
MQ_VERSION ?= 9.3.0.5
###########################################################################################################################################################

View File

@@ -1,4 +1,4 @@
# © Copyright IBM Corporation 2017, 2018 # © Copyright IBM Corporation 2020
# #
# 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,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.
[[constraint]] FROM fedora:32
name = "github.com/docker/docker" RUN yum install skopeo -y -qq
version = "=v17.03.2-ce" ENTRYPOINT [ "skopeo" ]
[[constraint]]
name = "github.com/docker/go-connections"
version = "0.4.0"
[prune]
go-tests = true

View File

@@ -4,33 +4,41 @@
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). If using Podman on macOS, the you need to be in "rootful" mode to allow the use of a network during builds. Run `podman machine init --rootful`.
* [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
This procedure works for building the MQ Continuous Delivery release, on `amd64`, `ppc64le` and `s390x` architectures. From MQ 9.2.X, the MQ container adds support for MQ Long Term Support (LTS) **production licensed** releases.
### Building MQ 9.3 Long Term Support (LTS) and Continuous Delivery (CD)
**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, 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
``` ```
### Building previous MQ Long Term Support (LTS)
**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).
However, if you wish to build the previous MQ LTS, use the [instructions](/../9.2.0.x/docs/building.md#mq-long-term-support-lts) in the `v9.2.0.x-eus` branch.
## 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 from IBM developerWorks. This is currently only available on the `amd64` architecture. 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).

View File

@@ -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:

View File

@@ -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.

View File

@@ -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.0.5-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
``` ```

View File

@@ -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:
@@ -14,17 +13,19 @@ There are two main sets of tests:
2. Docker tests, which test a complete Docker image, using the Docker API 2. Docker tests, which test a complete Docker image, using the Docker API
### Running the Docker tests ### Running the Docker 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 test-devserver make devserver
make test-advancedserver make 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.0.5-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:

View File

@@ -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/
``` ```

View File

@@ -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

24
go.mod Normal file
View File

@@ -0,0 +1,24 @@
module github.com/ibm-messaging/mq-container
go 1.19
require (
github.com/genuinetools/amicontained v0.4.3
github.com/ibm-messaging/mq-golang v2.0.0+incompatible
github.com/prometheus/client_golang v1.11.1
github.com/prometheus/client_model v0.2.0
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1
software.sslmate.com/src/go-pkcs12 v0.0.0-20200830195227-52f69702a001
)
require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.1.1 // indirect
github.com/golang/protobuf v1.4.3 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
github.com/prometheus/common v0.26.0 // indirect
github.com/prometheus/procfs v0.6.0 // indirect
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 // indirect
google.golang.org/protobuf v1.26.0-rc.1 // indirect
)

149
go.sum Normal file
View File

@@ -0,0 +1,149 @@
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
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/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-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
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.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
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/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/genuinetools/amicontained v0.4.3 h1:cqq9XiAHfWWY3dk8VU8bSJFu9yh8Il5coEdeTAPq72o=
github.com/genuinetools/amicontained v0.4.3/go.mod h1:PAMZkg9CcUTa6gNyULQ6tOMTMEb2HTKJufvKeFqDw+o=
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/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.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
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.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
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/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
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.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/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/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.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
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.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
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/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/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/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/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/reflect2 v0.0.0-20180701023420-4b7aa43c6742/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-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
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.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
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_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/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/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
github.com/prometheus/common v0.26.0 h1:iMAkS2TDoNWnKM+Kopnx/8tnEStIfpYA0ur0xQzzhMQ=
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4=
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
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/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/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
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/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/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-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/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-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/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-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-20190422165155-953cdadca894/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-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-20210124154548-22da62e12c0c/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/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/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
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-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.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
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=
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 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
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.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.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
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=

18
ha/native-ha.ini.tpl Normal file
View File

@@ -0,0 +1,18 @@
NativeHALocalInstance:
Name={{ .Name }}
{{ if .CertificateLabel }}
CertificateLabel={{ .CertificateLabel }}
KeyRepository={{ .KeyRepository }}
{{ if .CipherSpec }}
CipherSpec={{ .CipherSpec }}
{{- end }}
{{- end }}
NativeHAInstance:
Name={{ .NativeHAInstance0_Name }}
ReplicationAddress={{ .NativeHAInstance0_ReplicationAddress }}
NativeHAInstance:
Name={{ .NativeHAInstance1_Name }}
ReplicationAddress={{ .NativeHAInstance1_ReplicationAddress }}
NativeHAInstance:
Name={{ .NativeHAInstance2_Name }}
ReplicationAddress={{ .NativeHAInstance2_ReplicationAddress }}

View File

@@ -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)

View File

@@ -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

View File

@@ -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>

View File

@@ -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."

View File

@@ -1,6 +1,6 @@
#!/bin/bash #!/bin/bash
# -*- mode: sh -*- # -*- mode: sh -*-
# © Copyright IBM Corporation 2015, 2020 # © Copyright IBM Corporation 2015, 2021
# #
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
@@ -66,7 +66,7 @@ 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 +78,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

View File

@@ -1,6 +1,6 @@
#!/bin/bash #!/bin/bash
# -*- mode: sh -*- # -*- mode: sh -*-
# © Copyright IBM Corporation 2015, 2020 # © Copyright IBM Corporation 2015, 2022
# #
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,17 +21,11 @@ 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 # Download and extract the MQ unzippable server
DIR_TMP=/tmp/mq DIR_TMP=/tmp/mq
mkdir -p ${DIR_TMP} mkdir -p ${DIR_TMP}
cd ${DIR_TMP} cd ${DIR_TMP}
curl -LO $MQ_URL curl --fail --location $MQ_URL | tar --extract --gunzip
tar -xzf ./*.tar.gz
rm -f ./*.tar.gz
ls -la ${DIR_TMP} ls -la ${DIR_TMP}
# Generate MQ package in INSTALLATION_DIR # Generate MQ package in INSTALLATION_DIR
@@ -53,7 +47,7 @@ export genmqpkg_incmqxr=0
export genmqpkg_incnls=1 export genmqpkg_incnls=1
export genmqpkg_incras=1 export genmqpkg_incras=1
export genmqpkg_incsamp=1 export genmqpkg_incsamp=1
export genmqpkg_incsdk=$INSTALL_SDK export genmqpkg_incsdk=0
export genmqpkg_inctls=1 export genmqpkg_inctls=1
export genmqpkg_incunthrd=0 export genmqpkg_incunthrd=0
export genmqpkg_incweb=1 export genmqpkg_incweb=1
@@ -97,8 +91,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

View File

@@ -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)

68
internal/ha/ha.go Normal file
View File

@@ -0,0 +1,68 @@
/*
© Copyright IBM Corporation 2020, 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.
*/
// Package ha contains code for high availability
package ha
import (
"os"
"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
}
}
err := mqtemplate.ProcessTemplateFile(templateFile, file, templateMap, log)
if err != nil {
return err
}
return nil
}

View File

@@ -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
}

View File

@@ -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)
}
}

View File

@@ -1 +0,0 @@
guest:$2y$05$ifFP0nCmFed6.m4iB9CHRuHFps2YeeuwopmOvszWt0GRnN59p8qxW

View File

@@ -23,9 +23,10 @@ import (
"net/http" "net/http"
"time" "time"
"github.com/ibm-messaging/mq-container/pkg/logger"
"github.com/ibm-messaging/mq-container/internal/ready" "github.com/ibm-messaging/mq-container/internal/ready"
"github.com/ibm-messaging/mq-container/pkg/logger"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
) )
const ( const (
@@ -34,6 +35,8 @@ const (
var ( var (
metricsEnabled = false metricsEnabled = false
// #nosec G112 - this needs investigation to find reasonable timeout.
// git-issue 233 to cover this..
metricsServer = &http.Server{Addr: ":" + defaultPort} metricsServer = &http.Server{Addr: ":" + defaultPort}
) )
@@ -83,7 +86,7 @@ func startMetricsGathering(qmName string, log *logger.Logger) error {
} }
// Setup HTTP server to handle requests from Prometheus // Setup HTTP server to handle requests from Prometheus
http.Handle("/metrics", prometheus.Handler()) http.Handle("/metrics", promhttp.Handler())
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(200) w.WriteHeader(200)
// #nosec G104 // #nosec G104

View File

@@ -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 {

View File

@@ -0,0 +1,97 @@
/*
© 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 mqversion
import (
"fmt"
"strconv"
"strings"
"github.com/ibm-messaging/mq-container/internal/command"
)
// Get will return the current MQ version
func Get() (string, error) {
mqVersion, _, err := command.Run("dspmqver", "-b", "-f", "2")
if err != nil {
return "", fmt.Errorf("Error Getting MQ version: %v", err)
}
return strings.TrimSpace(mqVersion), nil
}
// Compare returns an integer comparing two MQ version strings lexicographically. The result will be 0 if currentVersion==checkVersion, -1 if currentVersion < checkVersion, and +1 if currentVersion > checkVersion
func Compare(checkVersion string) (int, error) {
currentVersion, err := Get()
if err != nil {
return 0, err
}
currentVRMF, err := parseVRMF(currentVersion)
if err != nil {
return 0, err
}
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
}

View File

@@ -0,0 +1,147 @@
/*
© 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 mqversion
import (
"fmt"
"testing"
)
func TestCompareLower(t *testing.T) {
checkVersion := "99.99.99.99"
mqVersionCheck, err := Compare(checkVersion)
if err != nil {
t.Fatalf("Failed to compare MQ versions: %v", err)
}
if mqVersionCheck != -1 {
t.Errorf("MQ version compare result failed. Expected -1, Got %v", mqVersionCheck)
}
}
func TestCompareHigher(t *testing.T) {
checkVersion := "1.1.1.1"
mqVersionCheck, err := Compare(checkVersion)
if err != nil {
t.Fatalf("Failed to compare MQ versions: %v", err)
}
if mqVersionCheck != 1 {
t.Errorf("MQ version compare result failed. Expected 1, Got %v", mqVersionCheck)
}
}
func TestCompareEqual(t *testing.T) {
checkVersion, err := Get()
if err != nil {
t.Fatalf("Failed to get current MQ version: %v", err)
}
mqVersionCheck, err := Compare(checkVersion)
if err != nil {
t.Fatalf("Failed to compare MQ versions: %v", err)
}
if mqVersionCheck != 0 {
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)
}
})
}
}

View File

@@ -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() {}

View File

@@ -53,6 +53,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)
} }
@@ -76,6 +77,11 @@ func IsRunningAsStandbyQM(name string) (bool, error) {
return isRunningQM(name, "(RUNNING AS STANDBY)") return isRunningQM(name, "(RUNNING AS STANDBY)")
} }
// IsRunningAsReplicaQM returns true if the queue manager is running in replica mode
func IsRunningAsReplicaQM(name string) (bool, error) {
return isRunningQM(name, "(REPLICA)")
}
func isRunningQM(name string, status string) (bool, error) { func isRunningQM(name string, status string) (bool, error) {
out, _, err := command.Run("dspmq", "-n", "-m", name) out, _, err := command.Run("dspmq", "-n", "-m", name)
if err != nil { if err != nil {

View File

@@ -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,43 @@ 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) (string, KeyStoreData, KeyStoreData, error) {
cmsKeystore, p12Truststore, err := generateAllKeystores()
// Create the CMS Keystore & PKCS#12 Truststore (if required)
tlsStore, err := generateAllKeystores(keystoreDir, p12TruststoreRequired)
if err != nil { if err != nil {
return "", cmsKeystore, p12Truststore, err return "", tlsStore.Keystore, tlsStore.Truststore, err
} }
// 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)
}
// 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)
} }
// ConfigureTLS configures TLS for the queue manager // ConfigureTLS configures TLS for the queue manager
@@ -137,8 +158,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) (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 +172,30 @@ 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 // Create the CMS Keystore
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)
p12Truststore.Keystore = keystore.NewPKCS12KeyStore(filepath.Join(keystoreDir, p12TruststoreName), p12Truststore.Password) if p12TruststoreRequired {
err = p12Truststore.Keystore.Create() p12Truststore.Keystore = keystore.NewPKCS12KeyStore(filepath.Join(keystoreDir, p12TruststoreName), p12Truststore.Password)
if err != nil { err = p12Truststore.Keystore.Create()
return cmsKeystore, p12Truststore, fmt.Errorf("Failed to create PKCS#12 Truststore: %v", err) if err != nil {
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 := ""
@@ -190,7 +214,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 +225,30 @@ 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
} }
// 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 +263,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,15 +290,17 @@ 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")
} }
// Add to known certificates for the PKCS#12 Truststore if tlsStore.Truststore.Keystore != nil {
err = addToKnownCertificates(block, p12Truststore, true) // Add to known certificates for the PKCS#12 Truststore
if err != nil { err = addToKnownCertificates(block, &tlsStore.Truststore, true)
return fmt.Errorf("Failed to add to know certificates for PKCS#12 Truststore") if err != nil {
return fmt.Errorf("Failed to add to know certificates for PKCS#12 Truststore")
}
} }
} }
} }
@@ -281,17 +308,17 @@ 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 +328,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 +363,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,10 +411,12 @@ 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")
} }
// Add to known certificates for the PKCS#12 Truststore if p12Truststore.Keystore != nil {
err = addToKnownCertificates(block, p12Truststore, true) // Add to known certificates for the PKCS#12 Truststore
if err != nil { err = addToKnownCertificates(block, p12Truststore, true)
return nil, nil, fmt.Errorf("Failed to add to know certificates for PKCS#12 Truststore") if err != nil {
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)
@@ -510,6 +539,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 +584,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)

View File

@@ -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,7 +60,7 @@ 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")

View File

@@ -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 {
l.log(debugLevel, fmt.Sprint(args...)) if l.json {
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 {
l.log(debugLevel, fmt.Sprintf(format, args...)) if l.json {
l.log(debugLevel, fmt.Sprintf(format, args...))
} else {
l.log(debugLevel, fmt.Sprintf("DEBUG: "+format, args...))
}
} }
} }

7
source-branch.env Normal file
View 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 ?= v9.3.0.x
###########################################################################################################################################################

114
test/docker/Gopkg.lock generated
View File

@@ -1,114 +0,0 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
branch = "master"
name = "github.com/Azure/go-ansiterm"
packages = [
".",
"winterm"
]
revision = "d6e3b3328b783f23731bc4d058875b0371ff8109"
[[projects]]
name = "github.com/Microsoft/go-winio"
packages = ["."]
revision = "7da180ee92d8bd8bb8c37fc560e673e6557c392f"
version = "v0.4.7"
[[projects]]
name = "github.com/Sirupsen/logrus"
packages = ["."]
revision = "c155da19408a8799da419ed3eeb0cb5db0ad5dbc"
version = "v1.0.5"
[[projects]]
name = "github.com/docker/distribution"
packages = [
"digest",
"reference"
]
revision = "48294d928ced5dd9b378f7fd7c6f5da3ff3f2c89"
version = "v2.6.2"
[[projects]]
name = "github.com/docker/docker"
packages = [
"api/types",
"api/types/blkiodev",
"api/types/container",
"api/types/events",
"api/types/filters",
"api/types/mount",
"api/types/network",
"api/types/reference",
"api/types/registry",
"api/types/strslice",
"api/types/swarm",
"api/types/time",
"api/types/versions",
"api/types/volume",
"client",
"pkg/jsonlog",
"pkg/jsonmessage",
"pkg/stdcopy",
"pkg/term",
"pkg/term/windows",
"pkg/tlsconfig"
]
revision = "f5ec1e2936dcbe7b5001c2b817188b095c700c27"
version = "v17.03.2-ce"
[[projects]]
name = "github.com/docker/go-connections"
packages = [
"nat",
"sockets",
"tlsconfig"
]
revision = "3ede32e2033de7505e6500d6c868c2b9ed9f169d"
version = "v0.3.0"
[[projects]]
name = "github.com/docker/go-units"
packages = ["."]
revision = "0dadbb0345b35ec7ef35e228dabb8de89a65bf52"
version = "v0.3.2"
[[projects]]
name = "github.com/pkg/errors"
packages = ["."]
revision = "645ef00459ed84a119197bfb8d8205042c6df63d"
version = "v0.8.0"
[[projects]]
branch = "master"
name = "golang.org/x/crypto"
packages = ["ssh/terminal"]
revision = "88942b9c40a4c9d203b82b3731787b672d6e809b"
[[projects]]
branch = "master"
name = "golang.org/x/net"
packages = [
"context",
"context/ctxhttp",
"proxy"
]
revision = "6078986fec03a1dcc236c34816c71b0e05018fda"
[[projects]]
branch = "master"
name = "golang.org/x/sys"
packages = [
"unix",
"windows"
]
revision = "13d03a9a82fba647c21a0ef8fba44a795d0f0835"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "c792836365447209421d5dc68a75fa77063408b8a6a2f9325b976581a0d60107"
solver-name = "gps-cdcl"
solver-version = 1

View File

@@ -1,7 +1,7 @@
// +build mqdev // +build mqdev
/* /*
© 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");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -34,7 +34,7 @@ import (
func TestDevGoldenPath(t *testing.T) { func TestDevGoldenPath(t *testing.T) {
t.Parallel() t.Parallel()
cli, err := client.NewEnvClient() cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -43,6 +43,7 @@ func TestDevGoldenPath(t *testing.T) {
Env: []string{ Env: []string{
"LICENSE=accept", "LICENSE=accept",
"MQ_QMGR_NAME=" + qm, "MQ_QMGR_NAME=" + qm,
"DEBUG=true",
}, },
} }
id := runContainerWithPorts(t, cli, &containerConfig, []int{9443}) id := runContainerWithPorts(t, cli, &containerConfig, []int{9443})
@@ -68,7 +69,7 @@ func TestDevGoldenPath(t *testing.T) {
func TestDevSecure(t *testing.T) { func TestDevSecure(t *testing.T) {
t.Parallel() t.Parallel()
cli, err := client.NewEnvClient() cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -82,6 +83,8 @@ func TestDevSecure(t *testing.T) {
"MQ_QMGR_NAME=" + qm, "MQ_QMGR_NAME=" + qm,
"MQ_APP_PASSWORD=" + appPassword, "MQ_APP_PASSWORD=" + appPassword,
"DEBUG=1", "DEBUG=1",
"WLP_LOGGING_MESSAGE_FORMAT=JSON",
"MQ_ENABLE_EMBEDDED_WEB_SERVER_LOG=true",
}, },
Image: imageName(), Image: imageName(),
} }
@@ -128,7 +131,7 @@ func TestDevSecure(t *testing.T) {
func TestDevWebDisabled(t *testing.T) { func TestDevWebDisabled(t *testing.T) {
t.Parallel() t.Parallel()
cli, err := client.NewEnvClient() cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -159,7 +162,7 @@ func TestDevWebDisabled(t *testing.T) {
func TestDevConfigDisabled(t *testing.T) { func TestDevConfigDisabled(t *testing.T) {
t.Parallel() t.Parallel()
cli, err := client.NewEnvClient() cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@@ -1,7 +1,7 @@
// +build mqdev // +build mqdev
/* /*
© 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.
@@ -48,8 +48,9 @@ var insecureTLSConfig *tls.Config = &tls.Config{
} }
func waitForWebReady(t *testing.T, cli *client.Client, ID string, tlsConfig *tls.Config) { func waitForWebReady(t *testing.T, cli *client.Client, 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,
}, },
@@ -63,13 +64,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))
} }
} }
} }

View File

@@ -1,5 +1,5 @@
/* /*
© Copyright IBM Corporation 2017, 2019 © 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.
@@ -38,7 +38,7 @@ import (
func TestLicenseNotSet(t *testing.T) { func TestLicenseNotSet(t *testing.T) {
t.Parallel() t.Parallel()
cli, err := client.NewEnvClient() cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -55,7 +55,7 @@ func TestLicenseNotSet(t *testing.T) {
func TestLicenseView(t *testing.T) { func TestLicenseView(t *testing.T) {
t.Parallel() t.Parallel()
cli, err := client.NewEnvClient() cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -79,7 +79,7 @@ func TestLicenseView(t *testing.T) {
//Check that when the container is stopped that the command endmqm has option -tp and x //Check that when the container is stopped that the command endmqm has option -tp and x
func TestEndMQMOpts(t *testing.T) { func TestEndMQMOpts(t *testing.T) {
t.Parallel() t.Parallel()
cli, err := client.NewEnvClient() cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -114,7 +114,7 @@ func TestGoldenPathNoMetrics(t *testing.T) {
// Actual test function for TestGoldenPathNoMetrics & TestGoldenPathWithMetrics // Actual test function for TestGoldenPathNoMetrics & TestGoldenPathWithMetrics
func goldenPath(t *testing.T, metric bool) { func goldenPath(t *testing.T, metric bool) {
cli, err := client.NewEnvClient() cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -137,7 +137,7 @@ func goldenPath(t *testing.T, metric bool) {
func TestSecurityVulnerabilities(t *testing.T) { func TestSecurityVulnerabilities(t *testing.T) {
t.Parallel() t.Parallel()
cli, err := client.NewEnvClient() cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -171,7 +171,7 @@ func TestSecurityVulnerabilities(t *testing.T) {
func utilTestNoQueueManagerName(t *testing.T, hostName string, expectedName string) { func utilTestNoQueueManagerName(t *testing.T, hostName string, expectedName string) {
search := "QMNAME(" + expectedName + ")" search := "QMNAME(" + expectedName + ")"
cli, err := client.NewEnvClient() cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -217,7 +217,7 @@ func TestWithVolumeNoMetrics(t *testing.T) {
// Actual test function for TestWithVolumeNoMetrics & TestWithVolumeAndMetrics // Actual test function for TestWithVolumeNoMetrics & TestWithVolumeAndMetrics
func withVolume(t *testing.T, metric bool) { func withVolume(t *testing.T, metric bool) {
cli, err := client.NewEnvClient() cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -261,7 +261,7 @@ func withVolume(t *testing.T, metric bool) {
// TestWithSplitVolumesLogsData starts a queue manager with separate log/data mounts // TestWithSplitVolumesLogsData starts a queue manager with separate log/data mounts
func TestWithSplitVolumesLogsData(t *testing.T) { func TestWithSplitVolumesLogsData(t *testing.T) {
cli, err := client.NewEnvClient() cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -281,7 +281,7 @@ func TestWithSplitVolumesLogsData(t *testing.T) {
// TestWithSplitVolumesLogsOnly starts a queue manager with a separate log mount // TestWithSplitVolumesLogsOnly starts a queue manager with a separate log mount
func TestWithSplitVolumesLogsOnly(t *testing.T) { func TestWithSplitVolumesLogsOnly(t *testing.T) {
cli, err := client.NewEnvClient() cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -299,7 +299,7 @@ func TestWithSplitVolumesLogsOnly(t *testing.T) {
// TestWithSplitVolumesDataOnly starts a queue manager with a separate data mount // TestWithSplitVolumesDataOnly starts a queue manager with a separate data mount
func TestWithSplitVolumesDataOnly(t *testing.T) { func TestWithSplitVolumesDataOnly(t *testing.T) {
cli, err := client.NewEnvClient() cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -320,7 +320,7 @@ func TestWithSplitVolumesDataOnly(t *testing.T) {
func TestNoVolumeWithRestart(t *testing.T) { func TestNoVolumeWithRestart(t *testing.T) {
t.Parallel() t.Parallel()
cli, err := client.NewEnvClient() cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -340,7 +340,7 @@ func TestNoVolumeWithRestart(t *testing.T) {
// where `runmqserver -i` is run to initialize the storage. Then the // where `runmqserver -i` is run to initialize the storage. Then the
// container can be run as normal. // container can be run as normal.
func TestVolumeRequiresRoot(t *testing.T) { func TestVolumeRequiresRoot(t *testing.T) {
cli, err := client.NewEnvClient() cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -403,7 +403,7 @@ func TestVolumeRequiresRoot(t *testing.T) {
func TestCreateQueueManagerFail(t *testing.T) { func TestCreateQueueManagerFail(t *testing.T) {
t.Parallel() t.Parallel()
cli, err := client.NewEnvClient() cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -436,7 +436,7 @@ func TestCreateQueueManagerFail(t *testing.T) {
func TestStartQueueManagerFail(t *testing.T) { func TestStartQueueManagerFail(t *testing.T) {
t.Parallel() t.Parallel()
cli, err := client.NewEnvClient() cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -472,7 +472,7 @@ func TestStartQueueManagerFail(t *testing.T) {
func TestVolumeUnmount(t *testing.T) { func TestVolumeUnmount(t *testing.T) {
t.Parallel() t.Parallel()
cli, err := client.NewEnvClient() cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -521,7 +521,7 @@ func TestVolumeUnmount(t *testing.T) {
func TestZombies(t *testing.T) { func TestZombies(t *testing.T) {
t.Parallel() t.Parallel()
cli, err := client.NewEnvClient() cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -558,7 +558,7 @@ func TestZombies(t *testing.T) {
func TestMQSC(t *testing.T) { func TestMQSC(t *testing.T) {
t.Parallel() t.Parallel()
cli, err := client.NewEnvClient() cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -605,7 +605,7 @@ func TestMQSC(t *testing.T) {
func TestLargeMQSC(t *testing.T) { func TestLargeMQSC(t *testing.T) {
t.Parallel() t.Parallel()
cli, err := client.NewEnvClient() cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -657,7 +657,7 @@ func TestLargeMQSC(t *testing.T) {
func TestRedactValidMQSC(t *testing.T) { func TestRedactValidMQSC(t *testing.T) {
t.Parallel() t.Parallel()
cli, err := client.NewEnvClient() cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -735,7 +735,7 @@ func TestRedactValidMQSC(t *testing.T) {
func TestRedactInvalidMQSC(t *testing.T) { func TestRedactInvalidMQSC(t *testing.T) {
t.Parallel() t.Parallel()
cli, err := client.NewEnvClient() cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -808,7 +808,7 @@ func TestRedactInvalidMQSC(t *testing.T) {
// tries to start a container based on that image, and checks that container terminates // tries to start a container based on that image, and checks that container terminates
func TestInvalidMQSC(t *testing.T) { func TestInvalidMQSC(t *testing.T) {
t.Parallel() t.Parallel()
cli, err := client.NewEnvClient() cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -842,7 +842,7 @@ func TestInvalidMQSC(t *testing.T) {
func TestSimpleMQIniMerge(t *testing.T) { func TestSimpleMQIniMerge(t *testing.T) {
t.Parallel() t.Parallel()
cli, err := client.NewEnvClient() cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -880,7 +880,7 @@ func TestSimpleMQIniMerge(t *testing.T) {
} }
func TestMultipleIniMerge(t *testing.T) { func TestMultipleIniMerge(t *testing.T) {
t.Parallel() t.Parallel()
cli, err := client.NewEnvClient() cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -929,7 +929,7 @@ func TestMultipleIniMerge(t *testing.T) {
} }
func TestMQIniMergeOnTheSameVolumeButTwoContainers(t *testing.T) { func TestMQIniMergeOnTheSameVolumeButTwoContainers(t *testing.T) {
cli, err := client.NewEnvClient() cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -991,7 +991,7 @@ func TestMQIniMergeOnTheSameVolumeButTwoContainers(t *testing.T) {
RUN chmod 0660 /etc/mqm/test1.ini RUN chmod 0660 /etc/mqm/test1.ini
USER 1001`, imageName())}, USER 1001`, imageName())},
{"test1.ini", {"test1.ini",
"Log:\n LogFilePages=5000"}, "Log:\n LogBufferPages=128"},
} }
secondImage := createImage(t, cli, filesSecondContainer) secondImage := createImage(t, cli, filesSecondContainer)
@@ -1011,7 +1011,7 @@ func TestMQIniMergeOnTheSameVolumeButTwoContainers(t *testing.T) {
waitForReady(t, cli, ctr2.ID) waitForReady(t, cli, ctr2.ID)
_, test2 := execContainer(t, cli, ctr2.ID, "", []string{"bash", "-c", catIniFileCommand}) _, test2 := execContainer(t, cli, ctr2.ID, "", []string{"bash", "-c", catIniFileCommand})
changedStanza := strings.Contains(test2, "LogFilePages=5000") changedStanza := strings.Contains(test2, "LogBufferPages=128")
//check if stanza that was merged in the first container doesnt exist in this one. //check if stanza that was merged in the first container doesnt exist in this one.
firstMergedStanza := strings.Contains(test2, "ApplicationTrace:\n ApplName=amqsact*\n Trace=OFF") firstMergedStanza := strings.Contains(test2, "ApplicationTrace:\n ApplName=amqsact*\n Trace=OFF")
@@ -1027,7 +1027,7 @@ func TestMQIniMergeOnTheSameVolumeButTwoContainers(t *testing.T) {
func TestReadiness(t *testing.T) { func TestReadiness(t *testing.T) {
t.Parallel() t.Parallel()
cli, err := client.NewEnvClient() cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -1087,9 +1087,10 @@ func TestReadiness(t *testing.T) {
} }
func TestErrorLogRotation(t *testing.T) { func TestErrorLogRotation(t *testing.T) {
t.Skipf("Skipping %v until test defect fixed", t.Name())
t.Parallel() t.Parallel()
cli, err := client.NewEnvClient() cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -1179,7 +1180,7 @@ func TestJSONLogFormatNoMetrics(t *testing.T) {
// Actual test function for TestJSONLogFormatWithMetrics & TestJSONLogFormatNoMetrics // Actual test function for TestJSONLogFormatWithMetrics & TestJSONLogFormatNoMetrics
func jsonLogFormat(t *testing.T, metric bool) { func jsonLogFormat(t *testing.T, metric bool) {
cli, err := client.NewEnvClient() cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -1215,7 +1216,7 @@ func jsonLogFormat(t *testing.T, metric bool) {
func TestBadLogFormat(t *testing.T) { func TestBadLogFormat(t *testing.T) {
t.Parallel() t.Parallel()
cli, err := client.NewEnvClient() cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -1239,7 +1240,7 @@ func TestBadLogFormat(t *testing.T) {
func TestMQJSONDisabled(t *testing.T) { func TestMQJSONDisabled(t *testing.T) {
t.SkipNow() t.SkipNow()
t.Parallel() t.Parallel()
cli, err := client.NewEnvClient() cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -1267,7 +1268,7 @@ func TestCorrectLicense(t *testing.T) {
t.Fatal("Required test environment variable 'EXPECTED_LICENSE' was not set.") t.Fatal("Required test environment variable 'EXPECTED_LICENSE' was not set.")
} }
cli, err := client.NewEnvClient() cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -1292,7 +1293,7 @@ func TestCorrectLicense(t *testing.T) {
func TestVersioning(t *testing.T) { func TestVersioning(t *testing.T) {
t.Parallel() t.Parallel()
cli, err := client.NewEnvClient() cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -1413,7 +1414,7 @@ func TestVersioning(t *testing.T) {
func TestTraceStrmqm(t *testing.T) { func TestTraceStrmqm(t *testing.T) {
t.Parallel() t.Parallel()
cli, err := client.NewEnvClient() cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -1433,3 +1434,85 @@ func TestTraceStrmqm(t *testing.T) {
t.Fatalf("No trace files found in trace directory /var/mqm/trace. RC=%d.", rc) t.Fatalf("No trace files found in trace directory /var/mqm/trace. RC=%d.", rc)
} }
} }
// utilTestHealthCheck is used by TestHealthCheck* to run a container with
// privileges enabled or disabled. Otherwise the same as the golden path tests.
func utilTestHealthCheck(t *testing.T, nonewpriv bool) {
t.Parallel()
cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil {
t.Fatal(err)
}
containerConfig := container.Config{
Env: []string{"LICENSE=accept", "MQ_QMGR_NAME=qm1"},
}
hostConfig := getDefaultHostConfig(t, cli)
hostConfig.SecurityOpt = append(hostConfig.SecurityOpt, fmt.Sprintf("no-new-privileges:%v", nonewpriv))
id := runContainerWithHostConfig(t, cli, &containerConfig, hostConfig)
defer cleanContainer(t, cli, id)
waitForReady(t, cli, id)
rc, out := execContainer(t, cli, id, "", []string{"chkmqhealthy"})
t.Log(out)
if rc != 0 {
t.Errorf("Expected chkmqhealthy to return with exit code 0; got \"%v\"", rc)
t.Logf("Output from chkmqhealthy:\n%v", out)
}
// Stop the container cleanly
stopContainer(t, cli, id)
}
// TestHealthCheckWithNoNewPrivileges tests golden path start/stop plus
// chkmqhealthy, when running in a container where no new privileges are
// allowed (i.e. setuid is disabled)
func TestHealthCheckWithNoNewPrivileges(t *testing.T) {
utilTestHealthCheck(t, true)
}
// TestHealthCheckWithNoNewPrivileges tests golden path start/stop plus
// chkmqhealthy when running in a container where new privileges are
// allowed (i.e. setuid is allowed)
// See https://github.com/ibm-messaging/mq-container/issues/428
func TestHealthCheckWithNewPrivileges(t *testing.T) {
utilTestHealthCheck(t, false)
}
// utilTestStartedCheck is used by TestStartedCheck* to run a container with
// privileges enabled or disabled. Otherwise the same as the golden path tests.
func utilTestStartedCheck(t *testing.T, nonewpriv bool) {
t.Parallel()
cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil {
t.Fatal(err)
}
containerConfig := container.Config{
Env: []string{"LICENSE=accept", "MQ_QMGR_NAME=qm1"},
}
hostConfig := getDefaultHostConfig(t, cli)
hostConfig.SecurityOpt = append(hostConfig.SecurityOpt, fmt.Sprintf("no-new-privileges:%v", nonewpriv))
id := runContainerWithHostConfig(t, cli, &containerConfig, hostConfig)
defer cleanContainer(t, cli, id)
waitForReady(t, cli, id)
rc, out := execContainer(t, cli, id, "", []string{"chkmqstarted"})
t.Log(out)
if rc != 0 {
t.Errorf("Expected chkmqstarted to return with exit code 0; got \"%v\"", rc)
t.Logf("Output from chkmqstarted:\n%v", out)
}
// Stop the container cleanly
stopContainer(t, cli, id)
}
// TestStartedCheckWithNoNewPrivileges tests golden path start/stop plus
// chkmqstarted, when running in a container where no new privileges are
// allowed (i.e. setuid is disabled)
func TestStartedCheckWithNoNewPrivileges(t *testing.T) {
utilTestStartedCheck(t, true)
}
// TestStartedCheckWithNoNewPrivileges tests golden path start/stop plus
// chkmqstarted when running in a container where new privileges are
// allowed (i.e. setuid is allowed)
// See https://github.com/ibm-messaging/mq-container/issues/428
func TestStartedCheckWithNewPrivileges(t *testing.T) {
utilTestStartedCheck(t, false)
}

View File

@@ -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.
@@ -267,20 +267,8 @@ func generateRandomUID() string {
return fmt.Sprint(rand.Intn(max-min) + min) return fmt.Sprint(rand.Intn(max-min) + min)
} }
// runContainerWithPorts creates and starts a container, exposing the specified ports on the host. // getDefaultHostConfig creates a HostConfig and populates it with the defaults used in testing
// If no image is specified in the container config, then the image name is retrieved from the TEST_IMAGE func getDefaultHostConfig(t *testing.T, cli *client.Client) *container.HostConfig {
// environment variable.
func runContainerWithPorts(t *testing.T, cli *client.Client, containerConfig *container.Config, ports []int) 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))
hostConfig := container.HostConfig{ hostConfig := container.HostConfig{
Binds: []string{ Binds: []string{
coverageBind(t), coverageBind(t),
@@ -299,6 +287,62 @@ func runContainerWithPorts(t *testing.T, cli *client.Client, containerConfig *co
} else { } else {
t.Logf("Detected MQ Advanced image - dropping all capabilities") 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 *client.Client, containerConfig *container.Config, hostConfig *container.HostConfig) 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 := network.NetworkingConfig{}
t.Logf("Running container (%s)", containerConfig.Image)
ctr, err := cli.ContainerCreate(context.Background(), containerConfig, hostConfig, &networkingConfig, t.Name())
if err != nil {
t.Fatal(err)
}
startContainer(t, cli, ctr.ID)
return ctr.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 *client.Client, containerConfig *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, 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)
ctr, err := cli.ContainerCreate(context.Background(), containerConfig, hostConfig, networkingConfig, containerName)
if err != nil {
t.Fatal(err)
}
startContainer(t, cli, ctr.ID)
return ctr.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 *client.Client, containerConfig *container.Config, ports []int) string {
hostConfig := getDefaultHostConfig(t, cli)
for _, p := range ports { for _, p := range ports {
port := nat.Port(fmt.Sprintf("%v/tcp", p)) port := nat.Port(fmt.Sprintf("%v/tcp", p))
hostConfig.PortBindings[port] = []nat.PortBinding{ hostConfig.PortBindings[port] = []nat.PortBinding{
@@ -307,14 +351,7 @@ func runContainerWithPorts(t *testing.T, cli *client.Client, containerConfig *co
}, },
} }
} }
networkingConfig := network.NetworkingConfig{} return runContainerWithHostConfig(t, cli, containerConfig, hostConfig)
t.Logf("Running container (%s)", containerConfig.Image)
ctr, err := cli.ContainerCreate(context.Background(), containerConfig, &hostConfig, &networkingConfig, t.Name())
if err != nil {
t.Fatal(err)
}
startContainer(t, cli, ctr.ID)
return ctr.ID
} }
// runContainer creates and starts a container. If no image is specified in // runContainer creates and starts a container. If no image is specified in
@@ -511,9 +548,14 @@ func getCoverageExitCode(t *testing.T, orig int64) int64 {
func waitForContainer(t *testing.T, cli *client.Client, ID string, timeout time.Duration) int64 { func waitForContainer(t *testing.T, cli *client.Client, ID string, timeout time.Duration) int64 {
c, cancel := context.WithTimeout(context.Background(), timeout) c, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel() defer cancel()
rc, err := cli.ContainerWait(c, ID) t.Logf("Waiting for container for %s", timeout)
if err != nil { okC, errC := cli.ContainerWait(c, ID, container.WaitConditionNotRunning)
var rc int64
select {
case err := <-errC:
t.Fatal(err) t.Fatal(err)
case ok := <-okC:
rc = ok.StatusCode
} }
if coverage() { if coverage() {
// COVERAGE: When running coverage, the exit code is written to a file, // COVERAGE: When running coverage, the exit code is written to a file,
@@ -526,6 +568,7 @@ func waitForContainer(t *testing.T, cli *client.Client, ID string, timeout time.
// execContainer runs a command in a running container, and returns the exit code and output // execContainer runs a command in a running container, and returns the exit code and output
func execContainer(t *testing.T, cli *client.Client, ID string, user string, cmd []string) (int, string) { func execContainer(t *testing.T, cli *client.Client, ID string, user string, cmd []string) (int, string) {
t.Logf("Running command: %v", cmd)
config := types.ExecConfig{ config := types.ExecConfig{
User: user, User: user,
Privileged: false, Privileged: false,
@@ -541,7 +584,7 @@ func execContainer(t *testing.T, cli *client.Client, ID string, user string, cmd
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
hijack, err := cli.ContainerExecAttach(context.Background(), resp.ID, config) hijack, err := cli.ContainerExecAttach(context.Background(), resp.ID, types.ExecStartCheck{})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -609,6 +652,9 @@ func waitForReady(t *testing.T, cli *client.Client, ID string) {
} else if rc == 10 { } else if rc == 10 {
t.Log("MQ Readiness: Queue Manager Running as Standby") t.Log("MQ Readiness: Queue Manager Running as Standby")
return return
} else if rc == 20 {
t.Log("MQ Readiness: Queue Manager Running as Replica")
return
} }
case <-ctx.Done(): case <-ctx.Done():
t.Fatal("Timed out waiting for container to become ready") t.Fatal("Timed out waiting for container to become ready")
@@ -645,7 +691,7 @@ func removeNetwork(t *testing.T, cli *client.Client, ID string) {
} }
func createVolume(t *testing.T, cli *client.Client, name string) types.Volume { func createVolume(t *testing.T, cli *client.Client, name string) types.Volume {
v, err := cli.VolumeCreate(context.Background(), volume.VolumesCreateBody{ v, err := cli.VolumeCreate(context.Background(), volume.VolumeCreateBody{
Driver: "local", Driver: "local",
DriverOpts: map[string]string{}, DriverOpts: map[string]string{},
Labels: map[string]string{}, Labels: map[string]string{},
@@ -785,12 +831,23 @@ func copyFromContainer(t *testing.T, cli *client.Client, id string, file string)
} }
func getPort(t *testing.T, cli *client.Client, ID string, port int) string { func getPort(t *testing.T, cli *client.Client, ID string, port int) string {
i, err := cli.ContainerInspect(context.Background(), ID) var inspectInfo types.ContainerJSON
if err != nil { var err error
t.Fatal(err) for attemptsRemaining := 3; attemptsRemaining > 0; attemptsRemaining-- {
inspectInfo, err = cli.ContainerInspect(context.Background(), ID)
if err != nil {
t.Fatal(err)
}
portNat := nat.Port(fmt.Sprintf("%d/tcp", port))
if inspectInfo.NetworkSettings.Ports[portNat] == nil || len(inspectInfo.NetworkSettings.Ports[portNat]) == 0 {
t.Log("Container port not yet bound")
time.Sleep(1 * time.Second)
continue
}
return inspectInfo.NetworkSettings.Ports[portNat][0].HostPort
} }
portNat := nat.Port(fmt.Sprintf("%d/tcp", port)) t.Fatal("Failed to get port")
return i.NetworkSettings.Ports[portNat][0].HostPort return ""
} }
func countLines(t *testing.T, r io.Reader) int { func countLines(t *testing.T, r io.Reader) int {
@@ -823,3 +880,12 @@ func countTarLines(t *testing.T, b []byte) int {
} }
return total return total
} }
func getMQVersion(t *testing.T, cli *client.Client) (string, error) {
inspect, _, err := cli.ImageInspectWithRaw(context.Background(), imageName())
if err != nil {
return "", err
}
version := inspect.ContainerConfig.Labels["version"]
return version, nil
}

40
test/docker/go.mod Normal file
View File

@@ -0,0 +1,40 @@
module github.com/ibm-messaging/mq-container/test/docker
go 1.19
require (
// Note: This is not actually Docker v17.12!
// Go modules require the use of semver, but Docker does not use semver and has not
// [opted-in to use Go modules](https://github.com/golang/go/wiki/Modules#can-a-module-consume-a-package-that-has-not-opted-in-to-modules)
// This means that when you `go get` Docker, you need to do so based on a commit,
// e.g. `go get -v github.com/docker/docker@420b1d36250f9cfdc561f086f25a213ecb669b6f`,
// which uses the commit for [Docker v19.03.15](https://github.com/moby/moby/releases/tag/v19.03.15)
// Go will then find the latest tag with a semver-compatible tag. In Docker's case,
// v17.12.0 is valid semver, but v18.09 and v19.03 are not.
// Also note: Docker v20.10 is valid semver, but the v20.10 client API requires use of Docker API
// version 1.41 on the server, which is currently too new for the version of Docker in Travis (Ubuntu Bionic)
github.com/docker/docker v17.12.0-ce-rc1.0.20210128214336-420b1d36250f+incompatible
github.com/docker/go-connections v0.4.0
)
require (
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
github.com/Microsoft/go-winio v0.5.1 // indirect
github.com/containerd/containerd v1.6.3 // indirect
github.com/docker/distribution v2.8.1+incompatible // indirect
github.com/docker/go-units v0.4.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/gorilla/mux v1.8.0 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/sirupsen/logrus v1.8.1 // indirect
golang.org/x/net v0.0.0-20211216030914-fe4d6282115f // indirect
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150 // indirect
google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa // indirect
google.golang.org/grpc v1.46.0 // indirect
google.golang.org/protobuf v1.27.1 // indirect
gotest.tools v2.2.0+incompatible // indirect
)

199
test/docker/go.sum Normal file
View File

@@ -0,0 +1,199 @@
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=
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY=
github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/containerd/containerd v1.6.3 h1:JfgUEIAH07xDWk6kqz0P3ArZt+KJ9YeihSC9uyFtSKg=
github.com/containerd/containerd v1.6.3/go.mod h1:gCVGrYRYFm2E8GmuUIbj/NGD7DLZQLzSJQazjVKDOig=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68=
github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v17.12.0-ce-rc1.0.20210128214336-420b1d36250f+incompatible h1:nhVo1udYfMj0Jsw0lnqrTjjf33aLpdgW9Wve9fHVzhQ=
github.com/docker/docker v17.12.0-ce-rc1.0.20210128214336-420b1d36250f+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/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.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
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/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
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.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 h1:rc3tiVYb5z54aKaDfakKn0dDjIyPpTtszkjuMzyt7ec=
github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
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/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
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-20190108225652-1e06a53dbb7e/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-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20211216030914-fe4d6282115f h1:hEYJvxw1lSnWIl8X9ofsYMklzaDs90JI2az5YMd4fPM=
golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/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-20181221193216-37e7f081c4d4/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-20201020160332-67f06af15bc9/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-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-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150 h1:xHms4gcpe1YE7A3yIllJXP16CMAGuqwO2lX1mTyyRRc=
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs=
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-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
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/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
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-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa h1:I0YcKz0I7OAhddo7ya8kMnvprhcWM045PmkBdMO9zN0=
google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
google.golang.org/grpc v1.46.0 h1:oCjezcn6g6A75TGoKYBPgKmVBLexhYLM6MebdrPApP8=
google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
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-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.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.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.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
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.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
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=

View File

@@ -32,7 +32,8 @@ var miEnv = []string{
// TestMultiInstanceStartStop creates 2 containers in a multi instance queue manager configuration // TestMultiInstanceStartStop creates 2 containers in a multi instance queue manager configuration
// 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) {
cli, err := client.NewEnvClient() t.Skipf("Skipping %v until test defect fixed", t.Name())
cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -74,7 +75,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, err := client.NewClientWithOpts(client.FromEnv)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -108,7 +109,7 @@ func TestMultiInstanceContainerStop(t *testing.T) {
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, err := client.NewEnvClient() cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -154,7 +155,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, err := client.NewClientWithOpts(client.FromEnv)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -173,7 +174,7 @@ 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, err := client.NewClientWithOpts(client.FromEnv)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -195,7 +196,7 @@ 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, err := client.NewClientWithOpts(client.FromEnv)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -217,7 +218,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, err := client.NewClientWithOpts(client.FromEnv)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@@ -0,0 +1,219 @@
/*
© 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.
*/
package main
import (
"testing"
"github.com/docker/docker/client"
)
// 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, err := client.NewClientWithOpts(client.FromEnv)
if err != nil {
t.Fatal(err)
}
version, err := getMQVersion(t, cli)
if err != nil {
t.Fatal(err)
}
if version < "9.2.2.0" {
t.Skipf("Skipping %s as test requires at least MQ 9.2.2.0, but image is version %s", t.Name(), version)
}
containerNames := [3]string{"QM1_1", "QM1_2", "QM1_3"}
qmReplicaIDs := [3]string{}
qmVolumes := []string{}
qmNetwork, err := createBridgeNetwork(cli, t)
if err != nil {
t.Fatal(err)
}
defer removeBridgeNetwork(cli, qmNetwork.ID)
for i := 0; i <= 2; i++ {
vol := createVolume(t, cli, containerNames[i])
defer removeVolume(t, cli, vol.Name)
qmVolumes = append(qmVolumes, vol.Name)
containerConfig := getNativeHAContainerConfig(containerNames[i], containerNames, defaultHAPort)
hostConfig := getHostConfig(t, 1, "", "", vol.Name)
networkingConfig := getNativeHANetworkConfig(qmNetwork.ID)
ctr := runContainerWithAllConfig(t, cli, &containerConfig, &hostConfig, &networkingConfig, containerNames[i])
defer cleanContainer(t, cli, ctr)
qmReplicaIDs[i] = ctr
}
waitForReadyHA(t, cli, qmReplicaIDs)
_, 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, err := client.NewClientWithOpts(client.FromEnv)
if err != nil {
t.Fatal(err)
}
version, err := getMQVersion(t, cli)
if err != nil {
t.Fatal(err)
}
if version < "9.2.2.0" {
t.Skipf("Skipping %s as test requires at least MQ 9.2.2.0, but image is version %s", t.Name(), version)
}
containerNames := [3]string{"QM1_1", "QM1_2", "QM1_3"}
qmReplicaIDs := [3]string{}
qmVolumes := []string{}
qmNetwork, err := createBridgeNetwork(cli, t)
if err != nil {
t.Fatal(err)
}
defer removeBridgeNetwork(cli, qmNetwork.ID)
for i := 0; i <= 2; i++ {
vol := createVolume(t, cli, containerNames[i])
defer removeVolume(t, cli, vol.Name)
qmVolumes = append(qmVolumes, vol.Name)
containerConfig := getNativeHAContainerConfig(containerNames[i], containerNames, defaultHAPort)
hostConfig := getHostConfig(t, 1, "", "", vol.Name)
networkingConfig := getNativeHANetworkConfig(qmNetwork.ID)
ctr := runContainerWithAllConfig(t, cli, &containerConfig, &hostConfig, &networkingConfig, containerNames[i])
defer cleanContainer(t, cli, ctr)
qmReplicaIDs[i] = ctr
}
waitForReadyHA(t, cli, qmReplicaIDs)
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, err := client.NewClientWithOpts(client.FromEnv)
if err != nil {
t.Fatal(err)
}
version, err := getMQVersion(t, cli)
if err != nil {
t.Fatal(err)
}
if version < "9.2.2.0" {
t.Skipf("Skipping %s as test requires at least MQ 9.2.2.0, but image is version %s", t.Name(), version)
}
containerNames := [3]string{"QM1_1", "QM1_2", "QM1_3"}
qmReplicaIDs := [3]string{}
qmNetwork, err := createBridgeNetwork(cli, t)
if err != nil {
t.Fatal(err)
}
defer removeBridgeNetwork(cli, qmNetwork.ID)
for i := 0; i <= 2; i++ {
containerConfig := getNativeHAContainerConfig(containerNames[i], containerNames, defaultHAPort)
containerConfig.Env = append(containerConfig.Env, "MQ_NATIVE_HA_TLS=true")
hostConfig := getNativeHASecureHostConfig(t)
networkingConfig := getNativeHANetworkConfig(qmNetwork.ID)
ctr := runContainerWithAllConfig(t, cli, &containerConfig, &hostConfig, &networkingConfig, containerNames[i])
defer cleanContainer(t, cli, ctr)
qmReplicaIDs[i] = ctr
}
waitForReadyHA(t, cli, qmReplicaIDs)
_, 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, err := client.NewClientWithOpts(client.FromEnv)
if err != nil {
t.Fatal(err)
}
version, err := getMQVersion(t, cli)
if err != nil {
t.Fatal(err)
}
if version < "9.2.2.0" {
t.Skipf("Skipping %s as test requires at least MQ 9.2.2.0, but image is version %s", t.Name(), version)
}
containerNames := [3]string{"QM1_1", "QM1_2", "QM1_3"}
qmReplicaIDs := [3]string{}
qmNetwork, err := createBridgeNetwork(cli, t)
if err != nil {
t.Fatal(err)
}
defer removeBridgeNetwork(cli, qmNetwork.ID)
for i := 0; i <= 2; i++ {
containerConfig := getNativeHAContainerConfig(containerNames[i], containerNames, defaultHAPort)
containerConfig.Env = append(containerConfig.Env, "MQ_NATIVE_HA_TLS=true", "MQ_NATIVE_HA_CIPHERSPEC=TLS_AES_256_GCM_SHA384")
hostConfig := getNativeHASecureHostConfig(t)
networkingConfig := getNativeHANetworkConfig(qmNetwork.ID)
ctr := runContainerWithAllConfig(t, cli, &containerConfig, &hostConfig, &networkingConfig, containerNames[i])
defer cleanContainer(t, cli, ctr)
qmReplicaIDs[i] = ctr
}
waitForReadyHA(t, cli, qmReplicaIDs)
_, err = getActiveReplicaInstances(t, cli, qmReplicaIDs)
if err != nil {
t.Fatal(err)
}
}

View File

@@ -0,0 +1,149 @@
/*
© 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.
*/
package main
import (
"context"
"fmt"
"path/filepath"
"testing"
"time"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/network"
"github.com/docker/docker/client"
)
const defaultHAPort = 9414
// HAReplicaStatus represents the Active/Replica/Replica container status of the queue manager
type HAReplicaStatus struct {
Active string
Replica [2]string
}
func createBridgeNetwork(cli *client.Client, t *testing.T) (types.NetworkCreateResponse, error) {
return cli.NetworkCreate(context.Background(), t.Name(), types.NetworkCreate{})
}
func removeBridgeNetwork(cli *client.Client, networkID string) error {
return cli.NetworkRemove(context.Background(), networkID)
}
func getNativeHAContainerConfig(containerName string, replicaNames [3]string, haPort int) container.Config {
return container.Config{
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)", replicaNames[0], haPort),
fmt.Sprintf("MQ_NATIVE_HA_INSTANCE_1_REPLICATION_ADDRESS=%s(%d)", replicaNames[1], haPort),
fmt.Sprintf("MQ_NATIVE_HA_INSTANCE_2_REPLICATION_ADDRESS=%s(%d)", replicaNames[2], haPort),
},
}
}
func getNativeHASecureHostConfig(t *testing.T) container.HostConfig {
return container.HostConfig{
Binds: []string{
coverageBind(t),
filepath.Join(getCwd(t, true), "../tls") + ":/etc/mqm/ha/pki/keys/ha",
},
}
}
func getNativeHANetworkConfig(networkID string) network.NetworkingConfig {
return network.NetworkingConfig{
EndpointsConfig: map[string]*network.EndpointSettings{
networkID: &network.EndpointSettings{},
},
}
}
func getActiveReplicaInstances(t *testing.T, cli *client.Client, 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 *client.Client, qmReplicaIDs [3]string) {
ctx, cancel := context.WithTimeout(context.Background(), 2*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 *client.Client, 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")
}
}
}

View File

@@ -1,5 +1,5 @@
/* /*
© 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");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -28,7 +28,7 @@ import (
func TestGoldenPathMetric(t *testing.T) { func TestGoldenPathMetric(t *testing.T) {
t.Parallel() t.Parallel()
cli, err := client.NewEnvClient() cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -55,7 +55,7 @@ func TestGoldenPathMetric(t *testing.T) {
func TestMetricNames(t *testing.T) { func TestMetricNames(t *testing.T) {
t.Parallel() t.Parallel()
cli, err := client.NewEnvClient() cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -100,7 +100,7 @@ func TestMetricLabels(t *testing.T) {
t.Parallel() t.Parallel()
requiredLabels := []string{"qmgr"} requiredLabels := []string{"qmgr"}
cli, err := client.NewEnvClient() cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -148,7 +148,7 @@ func TestMetricLabels(t *testing.T) {
func TestRapidFirePrometheus(t *testing.T) { func TestRapidFirePrometheus(t *testing.T) {
t.Parallel() t.Parallel()
cli, err := client.NewEnvClient() cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -182,7 +182,7 @@ func TestRapidFirePrometheus(t *testing.T) {
func TestSlowPrometheus(t *testing.T) { func TestSlowPrometheus(t *testing.T) {
t.Parallel() t.Parallel()
cli, err := client.NewEnvClient() cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -213,7 +213,7 @@ func TestSlowPrometheus(t *testing.T) {
func TestContainerRestart(t *testing.T) { func TestContainerRestart(t *testing.T) {
t.Parallel() t.Parallel()
cli, err := client.NewEnvClient() cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -261,7 +261,7 @@ func TestContainerRestart(t *testing.T) {
func TestQMRestart(t *testing.T) { func TestQMRestart(t *testing.T) {
t.Parallel() t.Parallel()
cli, err := client.NewEnvClient() cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -319,7 +319,7 @@ func TestQMRestart(t *testing.T) {
func TestValidValues(t *testing.T) { func TestValidValues(t *testing.T) {
t.Parallel() t.Parallel()
cli, err := client.NewEnvClient() cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -355,7 +355,7 @@ func TestValidValues(t *testing.T) {
func TestChangingValues(t *testing.T) { func TestChangingValues(t *testing.T) {
t.Parallel() t.Parallel()
cli, err := client.NewEnvClient() cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@@ -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");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
@@ -15,22 +15,24 @@
############################################################################### ###############################################################################
# Application build environment (Maven) # Application build environment (Maven)
############################################################################### ###############################################################################
FROM docker.io/maven:3-ibmjava as builder FROM registry.access.redhat.com/ubi8/openjdk-8 as builder
COPY pom.xml /usr/src/mymaven/ COPY pom.xml .
WORKDIR /usr/src/mymaven #WORKDIR /usr/src/mymaven
# Download dependencies separately, so Docker caches them # Download dependencies separately, so Docker caches them
RUN mvn dependency:go-offline install RUN mvn dependency:go-offline install
# Copy source # Copy source
COPY src /usr/src/mymaven/src COPY src .
# Run the main build # Run the main build
RUN mvn --offline install RUN mvn --offline install
# Print a list of all the files (useful for debugging) # Print a list of all the files (useful for debugging)
RUN find /usr/src/mymaven RUN find .
############################################################################### ###############################################################################
# Application runtime (JRE only, no build environment) # Application runtime (JRE only, no build environment)
############################################################################### ###############################################################################
FROM docker.io/ibmjava:8-jre # OpenJDK is not technically supported with the MQ client, but is good enough for these tests
COPY --from=builder /usr/src/mymaven/target/*.jar /opt/app/ FROM registry.access.redhat.com/ubi8/openjdk-8-runtime
COPY --from=builder /usr/src/mymaven/target/lib/*.jar /opt/app/ COPY --from=builder /home/jboss/target/*.jar /opt/app/
COPY --from=builder /home/jboss/target/lib/*.jar /opt/app/
USER 1001
ENTRYPOINT ["java", "-classpath", "/opt/app/*", "org.junit.platform.console.ConsoleLauncher", "-p", "com.ibm.mqcontainer.test", "--details", "verbose"] ENTRYPOINT ["java", "-classpath", "/opt/app/*", "org.junit.platform.console.ConsoleLauncher", "-p", "com.ibm.mqcontainer.test", "--details", "verbose"]

View File

@@ -1,5 +1,5 @@
<!-- <!--
© Copyright IBM Corporation 2018 © 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.
@@ -26,25 +26,25 @@ limitations under the License.
<dependency> <dependency>
<groupId>com.ibm.mq</groupId> <groupId>com.ibm.mq</groupId>
<artifactId>com.ibm.mq.allclient</artifactId> <artifactId>com.ibm.mq.allclient</artifactId>
<version>9.1.3.0</version> <version>9.3.0.0</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.junit.jupiter</groupId> <groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId> <artifactId>junit-jupiter-api</artifactId>
<version>5.5.2</version> <version>5.8.2</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.junit.jupiter</groupId> <groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId> <artifactId>junit-jupiter-engine</artifactId>
<version>5.5.2</version> <version>5.8.2</version>
<scope>runtime</scope> <scope>runtime</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.junit.platform</groupId> <groupId>org.junit.platform</groupId>
<artifactId>junit-platform-console-standalone</artifactId> <artifactId>junit-platform-console-standalone</artifactId>
<version>1.5.2</version> <version>1.8.2</version>
<scope>runtime</scope> <scope>runtime</scope>
</dependency> </dependency>
</dependencies> </dependencies>
@@ -65,5 +65,9 @@ limitations under the License.
</executions> </executions>
</plugin> </plugin>
</plugins> </plugins>
</build> </build>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>
</project> </project>

View File

@@ -1,5 +1,5 @@
/* /*
© Copyright IBM Corporation 2018 © 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.
@@ -15,7 +15,7 @@ limitations under the License.
*/ */
package com.ibm.mqcontainer.test; package com.ibm.mqcontainer.test;
import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.*;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
@@ -32,9 +32,12 @@ import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.TrustManagerFactory;
import com.ibm.mq.MQException;
import com.ibm.mq.constants.MQConstants;
import com.ibm.mq.jms.MQConnectionFactory; import com.ibm.mq.jms.MQConnectionFactory;
import com.ibm.mq.jms.MQQueue; import com.ibm.mq.jms.MQQueue;
import com.ibm.msg.client.wmq.WMQConstants; import com.ibm.msg.client.wmq.WMQConstants;
import com.ibm.msg.client.jms.DetailedJMSSecurityRuntimeException;
import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
@@ -57,27 +60,18 @@ class JMSTests {
static SSLSocketFactory createSSLSocketFactory() throws IOException, GeneralSecurityException { static SSLSocketFactory createSSLSocketFactory() throws IOException, GeneralSecurityException {
KeyStore ts=KeyStore.getInstance("jks"); KeyStore ts=KeyStore.getInstance("jks");
ts.load(new FileInputStream(TRUSTSTORE), PASSPHRASE.toCharArray()); ts.load(new FileInputStream(TRUSTSTORE), PASSPHRASE.toCharArray());
// KeyManagerFactory kmf=KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
TrustManagerFactory tmf=TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); TrustManagerFactory tmf=TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(ts); tmf.init(ts);
// tmf.init();
SSLContext ctx = SSLContext.getInstance("TLSv1.2"); SSLContext ctx = SSLContext.getInstance("TLSv1.2");
// Security.setProperty("crypto.policy", "unlimited");
ctx.init(null, tmf.getTrustManagers(), null); ctx.init(null, tmf.getTrustManagers(), null);
return ctx.getSocketFactory(); return ctx.getSocketFactory();
} }
static JMSContext create(String channel, String addr, String user, String password) throws JMSException, IOException, GeneralSecurityException { static MQConnectionFactory createMQConnectionFactory(String channel, String addr) throws JMSException, IOException, GeneralSecurityException {
LOGGER.info(String.format("Connecting to %s/TCP/%s(1414) as %s", channel, addr, user));
MQConnectionFactory factory = new MQConnectionFactory(); MQConnectionFactory factory = new MQConnectionFactory();
factory.setTransportType(WMQConstants.WMQ_CM_CLIENT); factory.setTransportType(WMQConstants.WMQ_CM_CLIENT);
factory.setChannel(channel); factory.setChannel(channel);
factory.setConnectionNameList(String.format("%s(1414)", addr)); factory.setConnectionNameList(String.format("%s(1414)", addr));
// If a password is set, make sure it gets sent to the queue manager for authentication
if (password != null) {
factory.setBooleanProperty(WMQConstants.USER_AUTHENTICATION_MQCSP, true);
}
// factory.setClientReconnectOptions(WMQConstants.WMQ_CLIENT_RECONNECT);
if (TRUSTSTORE == null) { if (TRUSTSTORE == null) {
LOGGER.info("Not using TLS"); LOGGER.info("Not using TLS");
} }
@@ -94,12 +88,33 @@ class JMSTests {
factory.setSSLCipherSuite("TLS_RSA_WITH_AES_128_CBC_SHA256"); factory.setSSLCipherSuite("TLS_RSA_WITH_AES_128_CBC_SHA256");
} }
} }
// Give up if unable to reconnect for 10 minutes return factory;
// factory.setClientReconnectTimeout(600); }
// LOGGER.info(String.format("user=%s pw=%s", user, password));
/**
* Create a JMSContext with the supplied user and password.
*/
static JMSContext create(String channel, String addr, String user, String password) throws JMSException, IOException, GeneralSecurityException {
LOGGER.info(String.format("Connecting to %s/TCP/%s(1414) as %s", channel, addr, user));
MQConnectionFactory factory = createMQConnectionFactory(channel, addr);
// If a password is set, make sure it gets sent to the queue manager for authentication
if (password != null) {
factory.setBooleanProperty(WMQConstants.USER_AUTHENTICATION_MQCSP, true);
}
LOGGER.info(String.format("CSP authentication: %s", factory.getBooleanProperty(WMQConstants.USER_AUTHENTICATION_MQCSP)));
return factory.createContext(user, password); return factory.createContext(user, password);
} }
/**
* Create a JMSContext with the default user identity (from the OS)
*/
static JMSContext create(String channel, String addr) throws JMSException, IOException, GeneralSecurityException {
LOGGER.info(String.format("Connecting to %s/TCP/%s(1414) as OS user '%s'", channel, addr, System.getProperty("user.name")));
MQConnectionFactory factory = createMQConnectionFactory(channel, addr);
LOGGER.info(String.format("CSP authentication: %s", factory.getBooleanProperty(WMQConstants.USER_AUTHENTICATION_MQCSP)));
return factory.createContext();
}
@BeforeAll @BeforeAll
private static void waitForQueueManager() { private static void waitForQueueManager() {
for (int i = 0; i < 20; i++) { for (int i = 0; i < 20; i++) {
@@ -116,28 +131,34 @@ class JMSTests {
} }
} }
@BeforeEach
void connect() throws Exception {
context = create(CHANNEL, ADDR, USER, PASSWORD);
}
@Test @Test
void succeedingTest(TestInfo t) throws JMSException { void putGetTest(TestInfo t) throws Exception {
context = create(CHANNEL, ADDR, USER, PASSWORD);
Queue queue = new MQQueue("DEV.QUEUE.1"); Queue queue = new MQQueue("DEV.QUEUE.1");
context.createProducer().send(queue, t.getDisplayName()); context.createProducer().send(queue, t.getDisplayName());
Message m = context.createConsumer(queue).receive(); Message m = context.createConsumer(queue).receive();
assertNotNull(m.getBody(String.class)); assertNotNull(m.getBody(String.class));
} }
// @Test
// void failingTest() {
// fail("a failing test");
// }
@Test @Test
@Disabled("for demonstration purposes") void defaultIdentityTest(TestInfo t) throws Exception {
void skippedTest() { LOGGER.info(String.format("Password='%s'", PASSWORD));
// not executed try {
// Don't pass a user/password, which should cause the default identity to be used
context = create(CHANNEL, ADDR);
} catch (DetailedJMSSecurityRuntimeException ex) {
Throwable cause = ex.getCause();
assertNotNull(cause);
assertTrue(cause instanceof MQException);
assertEquals(MQConstants.MQRC_NOT_AUTHORIZED, ((MQException)cause).getReason());
return;
}
// The default developer config allows any user to appear as "app", and use a blank password. This is done with the MCAUSER on the channel.
// If this test is run on a queue manager without a password set, then it should be possible to connect without exception.
// If this test is run on a queue manager with a password set, then an exception should be thrown, because this test doesn't send a password.
if ((PASSWORD != null) && (PASSWORD != "")) {
fail("Exception not thrown");
}
} }
@AfterEach @AfterEach
@@ -146,9 +167,4 @@ class JMSTests {
context.close(); context.close();
} }
} }
@AfterAll
static void tearDownAll() {
}
} }

Binary file not shown.

View File

@@ -1,6 +1,6 @@
#!/bin/bash #!/bin/bash -ex
# -*- mode: sh -*- # -*- mode: sh -*-
# © Copyright IBM Corporation 2018 # © 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.
@@ -23,6 +23,7 @@ PASSWORD=passw0rd
openssl req \ openssl req \
-newkey rsa:2048 -nodes -keyout ${KEY} \ -newkey rsa:2048 -nodes -keyout ${KEY} \
-subj "/CN=localhost" \ -subj "/CN=localhost" \
-addext "subjectAltName = DNS:localhost" \
-x509 -days 3650 -out ${CERT} -x509 -days 3650 -out ${CERT}
# Add the key and certificate to a PKCS #12 key store, for the server to use # Add the key and certificate to a PKCS #12 key store, for the server to use

View File

@@ -1,17 +1,17 @@
-----BEGIN CERTIFICATE----- -----BEGIN CERTIFICATE-----
MIICpDCCAYwCCQC6vpJFnfYO6TANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDDAls MIICwzCCAaugAwIBAgIJAKnwG0VGiDelMA0GCSqGSIb3DQEBCwUAMBQxEjAQBgNV
b2NhbGhvc3QwHhcNMTkwMzIxMTYxMzUxWhcNMjkwMzE4MTYxMzUxWjAUMRIwEAYD BAMMCWxvY2FsaG9zdDAeFw0yMTA5MDYxNTIyMDlaFw0zMTA5MDQxNTIyMDlaMBQx
VQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCu EjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
48qtIDwmihFqj2HY3dZjPfROA1MJ+D0c6aEA08ooOczthLB7XdZBQDapj8LFldyt ggEBALLmDX3OB/4DlzJzKz/Edc5qVdjdIN/u8pApSQPevT0mAsSK1uw2MObeOo4C
4ZMbTkqtF5QtPXmJY0wi39foLYlcGXPL1b7y3mypaFou88BcSM3VmfILKXhNeAlt DCBmabYeuvGzZ4t3SiejdsHK+qAYMFW51lxTbulv5kUPvTBOY2JCENkVDFjqcK8S
rXevnuT5kDU7sLVgKGhGwas20T1MU7d0I3bQ5z5c7egL76Hk9fYucjN6RkbwlrJ3 9ItI/UzTmnBolvZmUKzROHzA/pFb/jkhlzqJO+TqIBXKLF5gdFFTiHHcqfoUyVOV
TrCXrGIziofn3Zq1t51ygv21c80JD3XJ44YmuCrede4rhOS/4NpwRuZyiwpJ6tlv n+49V8z6W2rokz4QIWa5Dlh6VS1B6VXdihJv5P9HV8P8FOtefhA85yaSVKlFS/AC
0L0QSDGCmt2JT3ty28UAsGznFzC5Qu9KyaR+9Gk4aftiyKxrYWZkgtJmMRU+C1X2 XRb5FmtmYHBnghLktHS71s/KcPeX27Q1NcKhmZMvHRH95hqEcP25S6SGu69eiCLk
kFLOHsucGmJswjwubSR7AgMBAAEwDQYJKoZIhvcNAQELBQADggEBAEdlmXVGy86P xpbJKqG3fntfooLUDfR2PHQUJ7UCAwEAAaMYMBYwFAYDVR0RBA0wC4IJbG9jYWxo
XIX5a4ZmHQ5Ns4wm7rY8vzUxlymEQ86En1PN1zAO9gV94tLyNeMptjsFEEo/uJhC b3N0MA0GCSqGSIb3DQEBCwUAA4IBAQBamIH23oDh1XxOeMPUEyPLHm5M8LM8FNhT
Yvg3l5TIr/WCiY2+2XsSHvnbXrlbF3S0fRHa9VaCMRKjzRT68uq2Y891906YGtUE GEpf1ICy4TSLipSFhIs3hGzVt22zBBzU59apQ8rXUME5SK+9PLag/t/48rG1SfUA
m6fCjHqVzX8qaplDf79aVkPydYaYOIZ1a/mCfQcD9XMZ/v5zI9IUDhdoq97bgPhB VyYvaeu/cA5NQMWwuyCLqZL1MWn+BLsdAiNtbNHANesnl0i+vUb0GPzSP8soe3PP
gBOzWLI+hkzyU8jxKAFw1Hwi9lD/P6RXL5arNb/+arOgA3vTW+xGWGevgjVK1Ay9 N7Fh8SO3Qq6e9zT3iE2tP2OFxzcpg538Xn3qoVPJwmLIfBtvsiK07zqAWdqBWtt6
81beWiQmn0KbeLZxj+WJ9Nntlf1M4EqPYgsSYs/IlJTYS8W1B0mDJEoovPdFTryY cBXyagnmgKvOyv6sKAlTpwP9HqVem3XxZVrhm1KiPHs4Dnks6e79txmB8lqzvWu5
GyIuQEVcjUE= tu4h2ePGJjqUy5JkkoDY0j6hALwEe3ZXBvJ6XUQDi9Hou2k+MaQd
-----END CERTIFICATE----- -----END CERTIFICATE-----

View File

@@ -1,28 +1,28 @@
-----BEGIN PRIVATE KEY----- -----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCu48qtIDwmihFq MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCy5g19zgf+A5cy
j2HY3dZjPfROA1MJ+D0c6aEA08ooOczthLB7XdZBQDapj8LFldyt4ZMbTkqtF5Qt cys/xHXOalXY3SDf7vKQKUkD3r09JgLEitbsNjDm3jqOAgwgZmm2Hrrxs2eLd0on
PXmJY0wi39foLYlcGXPL1b7y3mypaFou88BcSM3VmfILKXhNeAltrXevnuT5kDU7 o3bByvqgGDBVudZcU27pb+ZFD70wTmNiQhDZFQxY6nCvEvSLSP1M05pwaJb2ZlCs
sLVgKGhGwas20T1MU7d0I3bQ5z5c7egL76Hk9fYucjN6RkbwlrJ3TrCXrGIziofn 0Th8wP6RW/45IZc6iTvk6iAVyixeYHRRU4hx3Kn6FMlTlZ/uPVfM+ltq6JM+ECFm
3Zq1t51ygv21c80JD3XJ44YmuCrede4rhOS/4NpwRuZyiwpJ6tlv0L0QSDGCmt2J uQ5YelUtQelV3YoSb+T/R1fD/BTrXn4QPOcmklSpRUvwAl0W+RZrZmBwZ4IS5LR0
T3ty28UAsGznFzC5Qu9KyaR+9Gk4aftiyKxrYWZkgtJmMRU+C1X2kFLOHsucGmJs u9bPynD3l9u0NTXCoZmTLx0R/eYahHD9uUukhruvXogi5MaWySqht357X6KC1A30
wjwubSR7AgMBAAECggEAH9t6teKjUlngJksMBdcTEGzerb9JRw2jBDtCisYJkx5E djx0FCe1AgMBAAECggEAagD5A49+mtwjzigB+4H80Def8KVuomIi5psgAaQM+9u3
SBfdlftX5fbufiCj2B4eXsYyZ8zxKWqcIUmLdA1Udx3TVIXG+bHhOAYtjEwb+xf5 DiC6ozKlHVeW2KiL6PLmNpzU5v0IINKpZP1uE/yjLxPGKDW6t/BUKww8JLXjw2jf
JYhdR/IzHG+4eXQKaAIvpXztyl3lU9iC+eaMg4GYzRrGN2wSAG9XgZ5cLF2TLJYU aMx+0TKwo0sfRA32S0YPmWNVAsBmm1AbA5vhXcK51QXuiInH406H5+d25ZJrYevF
jPxp7goz9X6V57aL2G/EFlbFsMaI/6cW7+XoRdo0I4N2Z766gz7GgyxtTVwR5Peq liKWSjx9CM/0XO7t20j18mCa8RjBEdsZoHxHsoWNvFJ6DCR25cFShAhR7s4OtkUk
LjOpqSNS0W57KJxReURfySok9CP1DfyigopsYW8O4jGVDDRLdiN3I8+JhWya2E0j yELm1tYYrFOffUM0Q/Fp9uSlCHWMSqPtf/6NEfnszfFEtzDh/N+YqC1Bexv0XPsD
96hHpN04Oz6HnMm7bdZDVtkZCOiu6xIzLJJxZ4o+kQKBgQDYqOA/hSod7s7w4LBE dBPOkUZjWA2Sc8Se1t2GLfrRURzj3GvWH1+GssjsAQKBgQDeIdyzQSqce4Kn0opa
A6Mp+e0//PYH6/N9SKmSIgQNec9bMGI4yanoblMbg4GM1g7pkvjlC0nTdjnUbLkB vdS5moCiv3pyfNd0nYe0awgVos4kHY7/nBq0eyMZAatRHeD3DunVsw3LmvWyEw53
vIvtVh3XwTIlrZ/4lc7VB23/hmKU+lRc+NJP5fgasAQu0W3+qp2cXo0pnHVwBEku app7MTTjVrYaadoBlB6jy2elyF5RcW2jGchZExoNh+0ZQWUiMbYEozPLQTy9ZxMz
Z7FwDPX0JNDIi/Or2I7dt8JojQKBgQDOpU1AnIXv1/cToYK4nz8BWLxRxwLTxy5A t0OcZ1hHPngGgmj5TELZKkwEtQKBgQDOLLh7pdKrdudtim+o4H20jl5yYKl25Iq/
ucafNKacPlxb5luZRCExiPZwAM8Z3zI9o99rYXOPQmsnknZWJV66Zx0Vo0yTD1CT DKVodwUd94cM7xAIOQJrx2XK/YPCfRkKRN1wxzAhYdIVkaaKDVhI8Jeu+H18QOa6
DWMUj0ugI1wORNMhwZP6YBYWjAeupyU9a7FyU1Geg4sdQt5rMyAEQOoECc8x8foP 5OlzzZcqJCtACpbVqLaDcmq8pRrAYekiwMIKwC95llvktjilvLfoUnQoXAaX8E8B
rySHuO/TJwKBgBjMM2ZxymFErQDa5rHSLMGoLmRtgodjlSnYwDfOluIn9/i67/MJ yCSUvDh3AQKBgQCxa0h04DLho4Da/D3HdmHHERF3bAqoEPCh0wTF5MsjRNLzY6yI
+d11iyOSCKji8y/+t2gXw6plVLcgfohZWTaf7ah9H006sx2Tn+m4APoHGo9sm21M mq11w/hni77C3mOF0SKRrh7xpcZiQfhHBx12EfpVLjfq5uraYe0LFHanol87G6bf
uV2Vt7DuRnxJUiqcwo9cLxH9K1/Xzbx299MYWKpJ8G+TvR8FGUz9NE4dAoGAM5gs I8Oy6Z/geNW2W1YktqHUGGpRCL0z5nUe1FyrOpv2431Ibbbcj73A6JipFQKBgHdl
KKSsAE1QwFMEG2qPRZvNMTHaL9w8XSbFQ7zWmI4tazihyCutifujZCWfj9sdZSyE vJyWpk73+AQe1JUnFIU4oYd5ZQpeRd9n8m5x5ru4+jPKSi2I3lcOTWvlrqU2Dwc8
PQBQ5QT1UiUMbMfZ1fqm1V83YERjnsOp6Fk6zZnmgx2GBZiahNn2ydxekqni72nz ZEUIhV3/qUsmYxy1p7ft5NnGO912NGhtYqjWmcEk2wsmVr17C99JpniC4OAik4G1
HRNWfphjZIPsmqFiLg2zIBz+4X6EK+RT35s6LeMCgYEAwF/9jX8kONW5KKZdoNHa wWm6bIPsSGFGCb4pcROQlIY+7O6WkxqEDnM4ITcBAoGAHXBKmadFpupUeGSkCwEo
opkLpa9qkwTGQ9M3AZiRUjM4rtvggYt8FBEP+3BLDLHqfUOkPq82MCRXm+6Cz+sT /VjeI4QoKKcWj9K8z8ifCVPz1FiQ1AJ91WMTM7PAmpEDX058Hor9xxJ2bEtQFwUS
gyPnsPlAh/sr3Pys3olJbUDE9H24k1LU0CI/sSwAFkka0+Q7PVTTe/Dcavitrcrm QKvjeU+/Ig0TWjsJBgBPvc0xYLaJptAbjvG4a5nBn7hwbRzLTcKx2OVTmdAkz00H
+fyiT2oSPZeHSjQE9iIW3OY= 1lq8cwizfwNgt8ldFFDDRvw=
-----END PRIVATE KEY----- -----END PRIVATE KEY-----

Binary file not shown.

View File

@@ -0,0 +1,249 @@
#!/bin/bash
# © 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.
usage="
Usage: artifact-util.sh -c my-registry.com/artifacts/my-project/builds/123 -u me@org.com -p top-secret -f tagcache -l ./.tagcache --upload \"
Where:
-c - Full artifact destination hostname and path
-u - The username to access repository
-p - The password or api-key to access repository
-f - Name of the file in repository
-l - The path and name to the file whose contents is to be pushed or retrieved into
Then one action of either
--check - Check if the file exists
--upload - Upload the contents of a file [-l must be specified]
--get - Get a file and write to a local file [-l must be specified]
--delete - Delet the remote file from repository
"
GREEN="\033[32m"
RED="\033[31m"
END="\033[0m"
RIGHTARROW="\xE2\x96\xB6"
BLUERIGHTARROW=${BLUE}${RIGHTARROW}${END}
GREENRIGHTARROW=${GREEN}${RIGHTARROW}${END}
ERROR=${RED}
TICK="\xE2\x9C\x94"
CROSS="\xE2\x9C\x97"
GREENTICK=${GREEN}${TICK}${END}
REDCROSS=${RED}${CROSS}${END}
SPACER="\n\n"
USER=
CREDENTIAL=
FILE_NAME=
BUILD_ID=
REGISTRY_HOSTNAME=
FILE_LOCATION=
PROPERTY_NAME=
CHECK=false
UPLOAD=false
GET=false
GET_PROPERTY=false
DELETE=false
DELETE_NAMESPACE=false
num_commands_selected=0
while getopts "f:u:p:c:l:n:-:" flag
do
case "${flag}" in
f) FILE_NAME=${OPTARG};;
u) USER=${OPTARG};;
p) CREDENTIAL=${OPTARG};;
c) CACHE_PATH=${OPTARG};;
l) FILE_LOCATION=${OPTARG};;
n) PROPERTY_NAME=${OPTARG};;
-)
case "${OPTARG}" in
check)
CHECK=true
num_commands_selected=$((num_commands_selected+1))
;;
upload)
UPLOAD=true
num_commands_selected=$((num_commands_selected+1))
;;
get)
GET=true
num_commands_selected=$((num_commands_selected+1))
;;
get-property)
GET_PROPERTY=true
num_commands_selected=$((num_commands_selected+1))
;;
delete)
DELETE=true
num_commands_selected=$((num_commands_selected+1))
;;
delete-namespace)
DELETE_NAMESPACE=true
num_commands_selected=$((num_commands_selected+1))
;;
*)
if [ "$OPTERR" = 1 ] && [ "${optspec:0:1}" != ":" ]; then
echo "Unknown option --${OPTARG}" >&2
fi
;;
esac;;
esac
done
if [[ $num_commands_selected == 0 || $num_commands_selected -gt 1 ]]; then
printf "${REDCROSS} ${ERROR}Too many actions specified. Should be one of ${END}--check${ERROR},${END} --get${ERROR},${END} --upload${ERROR} or${END} --delete${ERROR}!${END}\n"
printf $SPACER
printf "${ERROR}$usage${END}\n"
exit 1
fi
if [ "$DELETE_NAMESPACE" != "true" ]; then
if [[ -z $CACHE_PATH|| -z $USER || -z $CREDENTIAL || -z $FILE_NAME ]] ; then
printf "${REDCROSS} ${ERROR}Missing parameter!${END}\n"
printf "Cache Path:"$CACHE_PATH"\n"
printf "File name:"$FILE_NAME"\n"
printf "User":$USER"\n"
printf $SPACER
printf "${ERROR}$usage${END}\n"
exit 1
fi
fi
REMOTE_PATH="https://${CACHE_PATH}/$TRAVIS_BUILD_ID"
if [ "$CHECK" == "true" ]; then
printf "${GREENRIGHTARROW} Checking to see if file ${FILE_NAME} exists in repository ${REMOTE_PATH}\n"
FILE_FOUND=`curl -u ${USER}:${CREDENTIAL} -X GET "${REMOTE_PATH}/${FILE_NAME}" -o /dev/null -w "%{http_code}" -s`
if [ "$FILE_FOUND" != "200" ]; then
printf "${REDCROSS} File ${FILE_NAME} was not found\n"
exit 1
else
printf "${GREENTICK} File ${FILE_NAME} was found\n"
fi
fi
if [ "$UPLOAD" == "true" ]; then
printf "${GREENRIGHTARROW} Attempting to upload the file ${FILE_NAME} to repository ${REMOTE_PATH}\n"
if [[ -z $FILE_LOCATION ]]; then
printf "${REDCROSS} Location for ${FILE_NAME} was not supplied please do so\n"
printf $SPACER
printf "${ERROR}$usage${END}\n"
exit 1
fi
if [ ! -f "$FILE_LOCATION" ]; then
printf "${REDCROSS} Location supplied ${FILE_LOCATION } for file ${FILE_NAME} did not resolve to a file with contents to upload\n"
printf $SPACER
printf "${ERROR}$usage${END}\n"
exit 1
fi
curl -u ${USER}:${CREDENTIAL} -X PUT "$REMOTE_PATH/${FILE_NAME}" -T ${FILE_LOCATION}
fi
if [ "$GET" == "true" ]; then
printf "${GREENRIGHTARROW} Attempting to download file ${FILE_NAME} from repository ${REMOTE_PATH} to ${FILE_LOCATION}\n"
if [[ -z $FILE_LOCATION ]]; then
printf "${REDCROSS} Location for ${FILE_NAME} was not supplied please do so\n"
printf $SPACER
printf "${ERROR}$usage${END}\n"
exit 1
fi
curl -u ${USER}:${CREDENTIAL} "$REMOTE_PATH/${FILE_NAME}" -o ${FILE_LOCATION} -s
if [ $? != 0 ]; then
printf "${REDCROSS} Failed download\n"
else
printf "${GREENTICK} File ${FILE_NAME} was downloaded to ${FILE_LOCATION}\n"
fi
fi
if [ "$GET_PROPERTY" == "true" ]; then
if [[ -z $PROPERTY_NAME ]]; then
printf "${REDCROSS} Property name to retrieve from '${FILE_NAME}' was not supplied please do so\n"
printf $SPACER
printf "${ERROR}$usage${END}\n"
exit 1
fi
if [[ -z $FILE_LOCATION ]]; then
printf "${REDCROSS} File location to store property value in was not supplied please do so\n"
printf $SPACER
printf "${ERROR}$usage${END}\n"
exit 1
fi
printf "${GREENRIGHTARROW} Attempting to retrieve ${PROPERTY_NAME} of ${FILE_NAME} from repository ${REMOTE_PATH} and store it in ${FILE_LOCATION}\n"
query_url="${FILE_NAME}"
query_url="${query_url/\/artifactory\//\/artifactory\/api\/storage\//}?properties=${PROPERTY_NAME}"
request_result="$(curl -s -u ${USER}:${CREDENTIAL} "${query_url}")"
if [ $? != 0 ]; then
printf "Unable to retrieve properties from ${query_url}"
exit 1
else
printf "${GREENTICK} Properties retrieved from ${query_url}"
fi
jq -r '.properties.snapshot|first' <<<"$request_result" > ${FILE_LOCATION}
if [ $? != 0 ]; then
printf "Unable to write snapshot property to ${FILE_LOCATION}"
exit 1
else
printf "${GREENTICK} Property written to ${FILE_LOCATION}"
fi
fi
if [ "$DELETE" == "true" ]; then
printf "${GREENRIGHTARROW} Checking to see if file ${FILE_NAME} exists in repository ${REMOTE_PATH} before delete\n"
FILE_FOUND=`curl -u ${USER}:${CREDENTIAL} -X GET "${REMOTE_PATH}/${FILE_NAME}" -o /dev/null -w "%{http_code}" -s`
if [ "$FILE_FOUND" != "200" ]; then
printf "${REDCROSS} File ${FILE_NAME} was not found to delete\n"
exit 1
else
printf "${GREENTICK} File ${FILE_NAME} was found\n"
printf "${GREENRIGHTARROW} Attempting the delete of ${REMOTE_PATH}/${FILE_NAME}"
curl -u ${USER}:${CREDENTIAL} -X DELETE "${REMOTE_PATH}/${FILE_NAME}" -s
if [ $? != 0 ]; then
printf "${REDCROSS} Failed delete\n"
else
printf "${GREENTICK} File ${FILE_NAME} was deleted from "${REMOTE_PATH}"\n"
fi
fi
fi
if [ "$DELETE_NAMESPACE" == "true" ]; then
printf "${GREENRIGHTARROW} Checking to see if repository ${REMOTE_PATH} exists before delete\n"
DIR_FOUND=`curl -u ${USER}:${CREDENTIAL} -X GET "${REMOTE_PATH}" -o /dev/null -w "%{http_code}" -s`
if [ "$DIR_FOUND" != "200" ]; then
printf "${REDCROSS} Namespace ${REMOTE_PATH} was not found to delete\n"
exit 1
else
printf "${GREENTICK} Namespace ${REMOTE_PATH} was found\n"
printf "${GREENRIGHTARROW} Attempting the delete of ${REMOTE_PATH}"
curl -u ${USER}:${CREDENTIAL} -X DELETE "${REMOTE_PATH}" -s
if [ $? != 0 ]; then
printf "${REDCROSS} Failed delete\n"
else
printf "${GREENTICK} Namespace ${REMOTE_PATH} deleted \n"
fi
fi
fi
exit 0

View File

@@ -16,16 +16,68 @@
set -e set -e
echo 'Building Developer JMS test image...' && echo -en 'travis_fold:start:build-devjmstest\\r' archive_level_cache_dir="$(mktemp -d)"
make build-devjmstest
echo -en 'travis_fold:end:build-devjmstest\\r' get_archive_level() {
echo 'Building Developer image...' && echo -en 'travis_fold:start:build-devserver\\r' local level_path
make build-devserver local archive_variable
echo -en 'travis_fold:end:build-devserver\\r' archive_variable="$1"
if [ "$BUILD_ALL" = true ] ; then MQ_ARCHIVE_LEVEL=""
if [ "$ARCH" = "amd64" ] ; then level_path="${archive_level_cache_dir}/${archive_variable}.level"
echo 'Building Production image...' && echo -en 'travis_fold:start:build-advancedserver\\r'
make build-advancedserver if [[ ! -f "$level_path" ]]; then
echo -en 'travis_fold:end:build-advancedserver\\r' if [[ -z "${REPOSITORY_USER}" || -z "${REPOSITORY_CREDENTIAL}" ]]; then
echo 'Skipping level lookup as repository credentials not set'
return
fi fi
if [[ -z "${!archive_variable}" ]]; then
echo "Skipping level lookup as '\$${archive_variable}' is not set"
return
fi
./travis-build-scripts/artifact-util.sh -f "${!archive_variable}" -u "${REPOSITORY_USER}" -p "${REPOSITORY_CREDENTIAL}" -l "$level_path" -n snapshot --get-property
fi
read -r MQ_ARCHIVE_LEVEL < "$level_path"
export MQ_ARCHIVE_LEVEL
}
if [ "$TRAVIS_BRANCH" = "$MAIN_BRANCH" ] && [ "$TRAVIS_PULL_REQUEST" = "false" ]; then
echo 'Retrieving global tagcache' && echo -en 'travis_fold:start:tag-cache-retrieve\\r'
./travis-build-scripts/artifact-util.sh -c ${CACHE_PATH} -u ${REPOSITORY_USER} -p ${REPOSITORY_CREDENTIAL} -f cache/${TAGCACHE_FILE} -l ./.tagcache --check
./travis-build-scripts/artifact-util.sh -c ${CACHE_PATH} -u ${REPOSITORY_USER} -p ${REPOSITORY_CREDENTIAL} -f cache/${TAGCACHE_FILE} -l ./.tagcache --get
echo -en 'travis_fold:end:tag-cache-retrieve\\r'
fi
if [ -z "$BUILD_INTERNAL_LEVEL" ] ; then
if [ "$LTS" != true ] ; then
echo 'Building Developer JMS test image...' && echo -en 'travis_fold:start:build-devjmstest\\r'
make build-devjmstest
echo -en 'travis_fold:end:build-devjmstest\\r'
echo 'Building Developer image...' && echo -en 'travis_fold:start:build-devserver\\r'
get_archive_level MQ_ARCHIVE_REPOSITORY_DEV
make build-devserver
echo -en 'travis_fold:end:build-devserver\\r'
fi
if [ "$BUILD_ALL" = true ] || [ "$LTS" = true ] ; then
if [[ "$ARCH" = "amd64" || "$ARCH" = "s390x" || "$ARCH" = "ppc64le" ]] ; then
echo 'Building Production image...' && echo -en 'travis_fold:start:build-advancedserver\\r'
get_archive_level MQ_ARCHIVE_REPOSITORY
make build-advancedserver
echo -en 'travis_fold:end:build-advancedserver\\r'
fi
fi
else
echo 'Building Developer JMS test image...' && echo -en 'travis_fold:start:build-devjmstest\\r'
make build-devjmstest
echo -en 'travis_fold:end:build-devjmstest\\r'
if [[ "$BUILD_INTERNAL_LEVEL" == *".DE"* ]]; then
echo 'Building Developer image...' && echo -en 'travis_fold:start:build-devserver\\r'
get_archive_level MQ_ARCHIVE_REPOSITORY_DEV
make build-devserver
echo -en 'travis_fold:end:build-devserver\\r'
else
echo 'Building Production image...' && echo -en 'travis_fold:start:build-advancedserver\\r'
get_archive_level MQ_ARCHIVE_REPOSITORY
make build-advancedserver
echo -en 'travis_fold:end:build-advancedserver\\r'
fi
fi fi

View File

@@ -1,4 +1,6 @@
# © Copyright IBM Corporation 2018 #!/bin/bash
# © Copyright IBM Corporation 2020
# #
# 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,15 +14,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.
ARG BASE_IMAGE=mq-golang-build:9.0.5.0-x86_64-ubuntu-16.04 echo 'Cleaning up remote cache' && echo -en 'travis_fold:start:cleanup\\r'
./travis-build-scripts/artifact-util.sh -c ${CACHE_PATH} -u ${REPOSITORY_USER} -p ${REPOSITORY_CREDENTIAL} -f cache/${TAGCACHE_FILE} --delete
FROM $BASE_IMAGE echo -en 'travis_fold:end:cleanup\\r'
RUN mkdir -p "$GOPATH/src/github.com/ibm-messaging/mq-golang/samples"
WORKDIR $GOPATH/src/github.com/ibm-messaging/mq-golang/samples
COPY ./samples/clientconn clientconn
COPY ./samples/mqitest mqitest
RUN go install ./clientconn \
&& go install ./mqitest

View File

@@ -0,0 +1,90 @@
#!/bin/bash
# © 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.
set -e
usage="
Usage: create-image-manifest.sh -r hyc-mq-container-team-docker-local.artifactory.swg-devops.com -n foo -i ibm-mqadvanced-server-dev -t test -d \"sha256:038ad492532b099c324b897ce9da31ae0be312a1d0063f6456f2e3143cc4f4b8 sha256:754f466cf2cfc5183ac705689ce6720f27fecd07c97970ba3ec48769acba067d\"
Where:
-r - The image registry hostname
-n - The image registry namespace
-i - The image name
-t - The desired top level manifest tag
-d - A space separated list of sha256 image digests to be included
"
GREEN="\033[32m"
RED="\033[31m"
BLUE="\033[34m"
PURPLE="\033[35m"
AQUA="\033[36m"
END="\033[0m"
UNDERLINE="\033[4m"
BOLD="\033[1m"
ITALIC="\033[3m"
TITLE=${BLUE}${BOLD}${UNDERLINE}
STEPTITLE=${BLUERIGHTARROW}" "${BOLD}${ITALIC}
SUBSTEPTITLE=${MINIARROW}${MINIARROW}${MINIARROW}" "${ITALIC}
RIGHTARROW="\xE2\x96\xB6"
MINIARROW="\xE2\x96\xBB"
BLUERIGHTARROW=${BLUE}${RIGHTARROW}${END}
GREENRIGHTARROW=${GREEN}${RIGHTARROW}${END}
ERROR=${RED}
TICK="\xE2\x9C\x94"
CROSS="\xE2\x9C\x97"
GREENTICK=${GREEN}${TICK}${END}
REDCROSS=${RED}${CROSS}${END}
SPACER="\n\n"
while getopts r:n:i:t:d:h:u:p: flag
do
case "${flag}" in
r) REGISTRY=${OPTARG};;
n) NAMESPACE=${OPTARG};;
i) IMAGE=${OPTARG};;
t) TAG=${OPTARG};;
d) DIGESTS=${OPTARG};;
u) USER=${OPTARG};;
p) CREDENTIAL=${OPTARG};;
esac
done
if [[ -z $REGISTRY || -z $NAMESPACE || -z $IMAGE || -z $TAG || -z $DIGESTS ]] ; then
printf "${REDCROSS} ${ERROR}Missing parameter!${END}\n"
printf "${ERROR}$usage${END}\n"
exit 1
fi
# Docker CLI manifest commands require experimental features to be turned on
export DOCKER_CLI_EXPERIMENTAL=enabled
MANIFESTS=""
for digest in $DIGESTS ; do \
MANIFESTS+=" $REGISTRY/$NAMESPACE/$IMAGE@$digest"
done
docker login $REGISTRY -u $USER -p $CREDENTIAL
docker manifest create $REGISTRY/$NAMESPACE/$IMAGE:$TAG $MANIFESTS
MANIFEST_DIGEST=$(docker manifest push --purge $REGISTRY/$NAMESPACE/$IMAGE:$TAG)
echo $MANIFEST_DIGEST

View File

@@ -1,6 +1,7 @@
#!/bin/sh #!/bin/bash
# © Copyright IBM Corporation 2020
# #
# Copyright 2018 The Prometheus Authors
# 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.
# You may obtain a copy of the License at # You may obtain a copy of the License at
@@ -13,17 +14,11 @@
# 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.
check_license() { set -e
local file=""
for file in $(find . -type f -iname '*.go' ! -path './vendor/*'); do
head -n3 "${file}" | grep -Eq "(Copyright|generated|GENERATED)" || echo " ${file}"
done
}
licRes=$(check_license) echo 'Cacheing MQ tag...' && echo -en 'travis_fold:start:build-cache-mq-tag\\r'
make cache-mq-tag
if [ -n "${licRes}" ]; then echo -en 'travis_fold:end:cache-mq-tag\\r'
echo "license header checking failed:" echo 'Caching tagcache for future stages' && echo -en 'travis_fold:start:tag-cache\\r'
echo "${licRes}" ./travis-build-scripts/artifact-util.sh -c ${CACHE_PATH} -u ${REPOSITORY_USER} -p ${REPOSITORY_CREDENTIAL} -f cache/${TAGCACHE_FILE} -l ./.tagcache --upload
exit 255 echo -en 'travis_fold:end:tag-cache\\r'
fi

View File

@@ -19,15 +19,32 @@ sudo add-apt-repository "deb [arch=$ARCH] https://download.docker.com/linux/ubun
sudo apt update sudo apt update
sudo apt -y install docker-ce pass sudo apt -y install docker-ce pass
echo "default-cache-ttl 1200" > /home/travis/.gnupg/gpg-agent.conf
gpg-connect-agent reloadagent /bye
mkdir -p $GOPATH/src/github.com/docker mkdir -p $GOPATH/src/github.com/docker
cd $GOPATH/src/github.com/docker cd $GOPATH/src/github.com/docker
git clone https://github.com/docker/docker-credential-helpers git clone https://github.com/docker/docker-credential-helpers
cd docker-credential-helpers cd docker-credential-helpers
make pass
cp bin/docker-credential-pass $GOPATH/bin/docker-credential-pass # After https://github.com/docker/docker-credential-helpers/commit/fd0197473f0ecb29e73ccef9028057194ff463bc go 1.18 is required... Pin commit if earlier go installed
go_version="$(go version | cut -f3 -d' ')"
IFS=. read -a go_version_parts <<<"$go_version"
go_major="${go_version_parts[0]##go}"
go_minor="${go_version_parts[1]}"
if [[ "$go_major" -eq 1 && "$go_minor" -lt 18 ]]; then
echo "Go version ${go_major}.${go_minor} < 1.18... Pinning credential-helper commit"
git checkout ab7fd12c67d83193072fa91e5648b036547f6323
make pass
cp bin/docker-credential-pass $GOPATH/bin/docker-credential-pass
else
make pass
cp bin/build/docker-credential-pass $GOPATH/bin/docker-credential-pass
fi
mkdir -p /home/travis/.docker mkdir -p /home/travis/.docker
echo '{ "credsStore": "pass" }' | tee /home/travis/.docker/config.json echo '{ "credsStore": "pass" }' | tee /home/travis/.docker/config.json
gpg --batch --gen-key <<-EOF gpg2 --batch --gen-key <<-EOF
%echo generating a standard key %echo generating a standard key
Key-Type: DSA Key-Type: DSA
Key-Length: 1024 Key-Length: 1024
@@ -36,13 +53,14 @@ Subkey-Length: 1024
Name-Real: Travis CI Name-Real: Travis CI
Name-Email: travis@osism.io Name-Email: travis@osism.io
Expire-Date: 0 Expire-Date: 0
Passphrase: $REGISTRY_PASS
%commit %commit
%echo done %echo done
EOF EOF
key=$(gpg --no-auto-check-trustdb --list-secret-keys | grep ^sec | cut -d/ -f2 | cut -d" " -f1) key=$(gpg2 --list-secret-keys | grep uid -B 1 | head -n 1 | sed 's/^ *//g')
gpg --export-secret-keys | gpg2 --import -
pass init $key pass init $key
pass insert docker-credential-helpers/docker-pass-initialized-check <<-EOF pass insert docker-credential-helpers/docker-pass-initialized-check <<-EOF
pass is initialized pass is initialized
pass is initialized pass is initialized
EOF EOF
gpg2 --passphrase $REGISTRY_PASS --pinentry-mode=loopback --output doc --decrypt ~/.password-store/docker-credential-helpers/docker-pass-initialized-check.gpg

View File

@@ -1,6 +1,6 @@
#!/bin/bash #!/bin/bash
# © 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.
@@ -21,7 +21,7 @@ if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then
exit 0 exit 0
fi fi
if [ ! -z $2 ]; then if [ ! -z $2 ]; then
export ARCH=$2 export ARCH=$2
fi fi
@@ -32,11 +32,9 @@ function push_developer {
} }
function push_production { function push_production {
if [ "$ARCH" = "amd64" ] ; then echo 'Pushing Production image...' && echo -en 'travis_fold:start:push-advancedserver\\r'
echo 'Pushing Production image...' && echo -en 'travis_fold:start:push-advancedserver\\r' make push-advancedserver
make push-advancedserver echo -en 'travis_fold:end:push-advancedserver\\r'
echo -en 'travis_fold:end:push-advancedserver\\r'
fi
} }
# call relevant push function # call relevant push function
@@ -50,7 +48,7 @@ if [ ! -z $1 ]; then
exit 1 exit 1
;; ;;
esac esac
else else
echo "ERROR: Type ( developer | production ) must be passed to push.sh" echo "ERROR: Type ( developer | production ) must be passed to push.sh"
exit 1 exit 1
fi fi

View File

@@ -18,6 +18,18 @@ set -e
if [ "$(uname -m)" = "x86_64" ] ; then export ARCH="amd64" ; else export ARCH=$(uname -m) ; fi if [ "$(uname -m)" = "x86_64" ] ; then export ARCH="amd64" ; else export ARCH=$(uname -m) ; fi
# if DOCKER_USER is set, authenticate with docker.io to mitigate rate limit (https://www.docker.com/increase-rate-limits)
if [ -n "$DOCKER_USER" ] ; then echo 'Authenticating with docker.io...' && docker login -u $DOCKER_USER -p $DOCKER_PASS docker.io ; fi
if [ "$PUSH_MANIFEST_ONLY" = true ] ; then
echo 'Retrieving remote tagcache' && echo -en 'travis_fold:start:retrieve-tag-cache\\r'
./travis-build-scripts/artifact-util.sh -c ${CACHE_PATH} -u ${REPOSITORY_USER} -p ${REPOSITORY_CREDENTIAL} -f cache/${TAGCACHE_FILE} -l ./.tagcache --get
echo -en 'travis_fold:end:retrieve-tag-cache\\r'
make push-manifest
./travis-build-scripts/cleanup-cache.sh
exit 0
fi
echo 'Downgrading Docker (if necessary)...' && echo -en 'travis_fold:start:docker-downgrade\\r' echo 'Downgrading Docker (if necessary)...' && echo -en 'travis_fold:start:docker-downgrade\\r'
eval "$DOCKER_DOWNGRADE" eval "$DOCKER_DOWNGRADE"
echo -en 'travis_fold:end:docker-downgrade\\r' echo -en 'travis_fold:end:docker-downgrade\\r'
@@ -29,7 +41,19 @@ echo -en 'travis_fold:end:docker-downgrade\\r'
./travis-build-scripts/test.sh ./travis-build-scripts/test.sh
## Push images ## Push images
if [ "$BUILD_ALL" = true ] ; then if [ -z "$BUILD_INTERNAL_LEVEL" ] ; then
if [ "$BUILD_ALL" = true ] ; then
./travis-build-scripts/push.sh developer ./travis-build-scripts/push.sh developer
./travis-build-scripts/push.sh production ./travis-build-scripts/push.sh production
fi
else
if [[ "$BUILD_INTERNAL_LEVEL" == *".DE"* ]]; then
./travis-build-scripts/push.sh developer
else
./travis-build-scripts/push.sh production
fi
fi
if [ "$LTS" = true ] ; then
printf '\nIn CD stream but building LTS image. Do not push LTS image to artifactory\n'
fi fi

View File

@@ -16,15 +16,29 @@
set -e set -e
echo 'Testing Developer image...' && echo -en 'travis_fold:start:test-devserver\\r' if [ -z "$BUILD_INTERNAL_LEVEL" ] ; then
make test-devserver if [ "$LTS" != true ] ; then
echo -en 'travis_fold:end:test-devserver\\r' echo 'Testing Developer image...' && echo -en 'travis_fold:start:test-devserver\\r'
if [ "$BUILD_ALL" = true ] ; then make test-devserver
if [ "$ARCH" = "amd64" ] ; then echo -en 'travis_fold:end:test-devserver\\r'
echo 'Testing Production image...' && echo -en 'travis_fold:start:test-advancedserver\\r' fi
make test-advancedserver if [ "$BUILD_ALL" = true ] || [ "$LTS" = true ] ; then
echo -en 'travis_fold:end:test-advancedserver\\r' if [[ "$ARCH" = "amd64" || "$ARCH" = "s390x" || "$ARCH" = "ppc64le" ]] ; then
fi echo 'Testing Production image...' && echo -en 'travis_fold:start:test-advancedserver\\r'
make test-advancedserver
echo -en 'travis_fold:end:test-advancedserver\\r'
fi
fi
else
if [[ "$BUILD_INTERNAL_LEVEL" == *".DE"* ]]; then
echo 'Testing Developer image...' && echo -en 'travis_fold:start:test-devserver\\r'
make test-devserver
echo -en 'travis_fold:end:test-devserver\\r'
else
echo 'Testing Production image...' && echo -en 'travis_fold:start:test-advancedserver\\r'
make test-advancedserver
echo -en 'travis_fold:end:test-advancedserver\\r'
fi
fi fi
echo 'Running gosec scan...' && echo -en 'travis_fold:start:gosec-scan\\r' echo 'Running gosec scan...' && echo -en 'travis_fold:start:gosec-scan\\r'
if [ "$ARCH" = "amd64" ] ; then if [ "$ARCH" = "amd64" ] ; then

View File

@@ -1,2 +0,0 @@
*.test
*.prof

View File

@@ -1,31 +0,0 @@
# Perks for Go (golang.org)
Perks contains the Go package quantile that computes approximate quantiles over
an unbounded data stream within low memory and CPU bounds.
For more information and examples, see:
http://godoc.org/github.com/bmizerany/perks
A very special thank you and shout out to Graham Cormode (Rutgers University),
Flip Korn (AT&T LabsResearch), S. Muthukrishnan (Rutgers University), and
Divesh Srivastava (AT&T LabsResearch) for their research and publication of
[Effective Computation of Biased Quantiles over Data Streams](http://www.cs.rutgers.edu/~muthu/bquant.pdf)
Thank you, also:
* Armon Dadgar (@armon)
* Andrew Gerrand (@nf)
* Brad Fitzpatrick (@bradfitz)
* Keith Rarick (@kr)
FAQ:
Q: Why not move the quantile package into the project root?
A: I want to add more packages to perks later.
Copyright (C) 2013 Blake Mizerany
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -1,26 +0,0 @@
package histogram
import (
"math/rand"
"testing"
)
func BenchmarkInsert10Bins(b *testing.B) {
b.StopTimer()
h := New(10)
b.StartTimer()
for i := 0; i < b.N; i++ {
f := rand.ExpFloat64()
h.Insert(f)
}
}
func BenchmarkInsert100Bins(b *testing.B) {
b.StopTimer()
h := New(100)
b.StartTimer()
for i := 0; i < b.N; i++ {
f := rand.ExpFloat64()
h.Insert(f)
}
}

View File

@@ -1,108 +0,0 @@
// Package histogram provides a Go implementation of BigML's histogram package
// for Clojure/Java. It is currently experimental.
package histogram
import (
"container/heap"
"math"
"sort"
)
type Bin struct {
Count int
Sum float64
}
func (b *Bin) Update(x *Bin) {
b.Count += x.Count
b.Sum += x.Sum
}
func (b *Bin) Mean() float64 {
return b.Sum / float64(b.Count)
}
type Bins []*Bin
func (bs Bins) Len() int { return len(bs) }
func (bs Bins) Less(i, j int) bool { return bs[i].Mean() < bs[j].Mean() }
func (bs Bins) Swap(i, j int) { bs[i], bs[j] = bs[j], bs[i] }
func (bs *Bins) Push(x interface{}) {
*bs = append(*bs, x.(*Bin))
}
func (bs *Bins) Pop() interface{} {
return bs.remove(len(*bs) - 1)
}
func (bs *Bins) remove(n int) *Bin {
if n < 0 || len(*bs) < n {
return nil
}
x := (*bs)[n]
*bs = append((*bs)[:n], (*bs)[n+1:]...)
return x
}
type Histogram struct {
res *reservoir
}
func New(maxBins int) *Histogram {
return &Histogram{res: newReservoir(maxBins)}
}
func (h *Histogram) Insert(f float64) {
h.res.insert(&Bin{1, f})
h.res.compress()
}
func (h *Histogram) Bins() Bins {
return h.res.bins
}
type reservoir struct {
n int
maxBins int
bins Bins
}
func newReservoir(maxBins int) *reservoir {
return &reservoir{maxBins: maxBins}
}
func (r *reservoir) insert(bin *Bin) {
r.n += bin.Count
i := sort.Search(len(r.bins), func(i int) bool {
return r.bins[i].Mean() >= bin.Mean()
})
if i < 0 || i == r.bins.Len() {
// TODO(blake): Maybe use an .insert(i, bin) instead of
// performing the extra work of a heap.Push.
heap.Push(&r.bins, bin)
return
}
r.bins[i].Update(bin)
}
func (r *reservoir) compress() {
for r.bins.Len() > r.maxBins {
minGapIndex := -1
minGap := math.MaxFloat64
for i := 0; i < r.bins.Len()-1; i++ {
gap := gapWeight(r.bins[i], r.bins[i+1])
if minGap > gap {
minGap = gap
minGapIndex = i
}
}
prev := r.bins[minGapIndex]
next := r.bins.remove(minGapIndex + 1)
prev.Update(next)
}
}
func gapWeight(prev, next *Bin) float64 {
return next.Mean() - prev.Mean()
}

View File

@@ -1,38 +0,0 @@
package histogram
import (
"math/rand"
"testing"
)
func TestHistogram(t *testing.T) {
const numPoints = 1e6
const maxBins = 3
h := New(maxBins)
for i := 0; i < numPoints; i++ {
f := rand.ExpFloat64()
h.Insert(f)
}
bins := h.Bins()
if g := len(bins); g > maxBins {
t.Fatalf("got %d bins, wanted <= %d", g, maxBins)
}
for _, b := range bins {
t.Logf("%+v", b)
}
if g := count(h.Bins()); g != numPoints {
t.Fatalf("binned %d points, wanted %d", g, numPoints)
}
}
func count(bins Bins) int {
binCounts := 0
for _, b := range bins {
binCounts += b.Count
}
return binCounts
}

View File

@@ -1,63 +0,0 @@
package quantile
import (
"testing"
)
func BenchmarkInsertTargeted(b *testing.B) {
b.ReportAllocs()
s := NewTargeted(Targets)
b.ResetTimer()
for i := float64(0); i < float64(b.N); i++ {
s.Insert(i)
}
}
func BenchmarkInsertTargetedSmallEpsilon(b *testing.B) {
s := NewTargeted(TargetsSmallEpsilon)
b.ResetTimer()
for i := float64(0); i < float64(b.N); i++ {
s.Insert(i)
}
}
func BenchmarkInsertBiased(b *testing.B) {
s := NewLowBiased(0.01)
b.ResetTimer()
for i := float64(0); i < float64(b.N); i++ {
s.Insert(i)
}
}
func BenchmarkInsertBiasedSmallEpsilon(b *testing.B) {
s := NewLowBiased(0.0001)
b.ResetTimer()
for i := float64(0); i < float64(b.N); i++ {
s.Insert(i)
}
}
func BenchmarkQuery(b *testing.B) {
s := NewTargeted(Targets)
for i := float64(0); i < 1e6; i++ {
s.Insert(i)
}
b.ResetTimer()
n := float64(b.N)
for i := float64(0); i < n; i++ {
s.Query(i / n)
}
}
func BenchmarkQuerySmallEpsilon(b *testing.B) {
s := NewTargeted(TargetsSmallEpsilon)
for i := float64(0); i < 1e6; i++ {
s.Insert(i)
}
b.ResetTimer()
n := float64(b.N)
for i := float64(0); i < n; i++ {
s.Query(i / n)
}
}

View File

@@ -1,121 +0,0 @@
// +build go1.1
package quantile_test
import (
"bufio"
"fmt"
"log"
"os"
"strconv"
"time"
"github.com/beorn7/perks/quantile"
)
func Example_simple() {
ch := make(chan float64)
go sendFloats(ch)
// Compute the 50th, 90th, and 99th percentile.
q := quantile.NewTargeted(map[float64]float64{
0.50: 0.005,
0.90: 0.001,
0.99: 0.0001,
})
for v := range ch {
q.Insert(v)
}
fmt.Println("perc50:", q.Query(0.50))
fmt.Println("perc90:", q.Query(0.90))
fmt.Println("perc99:", q.Query(0.99))
fmt.Println("count:", q.Count())
// Output:
// perc50: 5
// perc90: 16
// perc99: 223
// count: 2388
}
func Example_mergeMultipleStreams() {
// Scenario:
// We have multiple database shards. On each shard, there is a process
// collecting query response times from the database logs and inserting
// them into a Stream (created via NewTargeted(0.90)), much like the
// Simple example. These processes expose a network interface for us to
// ask them to serialize and send us the results of their
// Stream.Samples so we may Merge and Query them.
//
// NOTES:
// * These sample sets are small, allowing us to get them
// across the network much faster than sending the entire list of data
// points.
//
// * For this to work correctly, we must supply the same quantiles
// a priori the process collecting the samples supplied to NewTargeted,
// even if we do not plan to query them all here.
ch := make(chan quantile.Samples)
getDBQuerySamples(ch)
q := quantile.NewTargeted(map[float64]float64{0.90: 0.001})
for samples := range ch {
q.Merge(samples)
}
fmt.Println("perc90:", q.Query(0.90))
}
func Example_window() {
// Scenario: We want the 90th, 95th, and 99th percentiles for each
// minute.
ch := make(chan float64)
go sendStreamValues(ch)
tick := time.NewTicker(1 * time.Minute)
q := quantile.NewTargeted(map[float64]float64{
0.90: 0.001,
0.95: 0.0005,
0.99: 0.0001,
})
for {
select {
case t := <-tick.C:
flushToDB(t, q.Samples())
q.Reset()
case v := <-ch:
q.Insert(v)
}
}
}
func sendStreamValues(ch chan float64) {
// Use your imagination
}
func flushToDB(t time.Time, samples quantile.Samples) {
// Use your imagination
}
// This is a stub for the above example. In reality this would hit the remote
// servers via http or something like it.
func getDBQuerySamples(ch chan quantile.Samples) {}
func sendFloats(ch chan<- float64) {
f, err := os.Open("exampledata.txt")
if err != nil {
log.Fatal(err)
}
sc := bufio.NewScanner(f)
for sc.Scan() {
b := sc.Bytes()
v, err := strconv.ParseFloat(string(b), 64)
if err != nil {
log.Fatal(err)
}
ch <- v
}
if sc.Err() != nil {
log.Fatal(sc.Err())
}
close(ch)
}

View File

@@ -1,215 +0,0 @@
package quantile
import (
"math"
"math/rand"
"sort"
"testing"
)
var (
Targets = map[float64]float64{
0.01: 0.001,
0.10: 0.01,
0.50: 0.05,
0.90: 0.01,
0.99: 0.001,
}
TargetsSmallEpsilon = map[float64]float64{
0.01: 0.0001,
0.10: 0.001,
0.50: 0.005,
0.90: 0.001,
0.99: 0.0001,
}
LowQuantiles = []float64{0.01, 0.1, 0.5}
HighQuantiles = []float64{0.99, 0.9, 0.5}
)
const RelativeEpsilon = 0.01
func verifyPercsWithAbsoluteEpsilon(t *testing.T, a []float64, s *Stream) {
sort.Float64s(a)
for quantile, epsilon := range Targets {
n := float64(len(a))
k := int(quantile * n)
if k < 1 {
k = 1
}
lower := int((quantile - epsilon) * n)
if lower < 1 {
lower = 1
}
upper := int(math.Ceil((quantile + epsilon) * n))
if upper > len(a) {
upper = len(a)
}
w, min, max := a[k-1], a[lower-1], a[upper-1]
if g := s.Query(quantile); g < min || g > max {
t.Errorf("q=%f: want %v [%f,%f], got %v", quantile, w, min, max, g)
}
}
}
func verifyLowPercsWithRelativeEpsilon(t *testing.T, a []float64, s *Stream) {
sort.Float64s(a)
for _, qu := range LowQuantiles {
n := float64(len(a))
k := int(qu * n)
lowerRank := int((1 - RelativeEpsilon) * qu * n)
upperRank := int(math.Ceil((1 + RelativeEpsilon) * qu * n))
w, min, max := a[k-1], a[lowerRank-1], a[upperRank-1]
if g := s.Query(qu); g < min || g > max {
t.Errorf("q=%f: want %v [%f,%f], got %v", qu, w, min, max, g)
}
}
}
func verifyHighPercsWithRelativeEpsilon(t *testing.T, a []float64, s *Stream) {
sort.Float64s(a)
for _, qu := range HighQuantiles {
n := float64(len(a))
k := int(qu * n)
lowerRank := int((1 - (1+RelativeEpsilon)*(1-qu)) * n)
upperRank := int(math.Ceil((1 - (1-RelativeEpsilon)*(1-qu)) * n))
w, min, max := a[k-1], a[lowerRank-1], a[upperRank-1]
if g := s.Query(qu); g < min || g > max {
t.Errorf("q=%f: want %v [%f,%f], got %v", qu, w, min, max, g)
}
}
}
func populateStream(s *Stream) []float64 {
a := make([]float64, 0, 1e5+100)
for i := 0; i < cap(a); i++ {
v := rand.NormFloat64()
// Add 5% asymmetric outliers.
if i%20 == 0 {
v = v*v + 1
}
s.Insert(v)
a = append(a, v)
}
return a
}
func TestTargetedQuery(t *testing.T) {
rand.Seed(42)
s := NewTargeted(Targets)
a := populateStream(s)
verifyPercsWithAbsoluteEpsilon(t, a, s)
}
func TestTargetedQuerySmallSampleSize(t *testing.T) {
rand.Seed(42)
s := NewTargeted(TargetsSmallEpsilon)
a := []float64{1, 2, 3, 4, 5}
for _, v := range a {
s.Insert(v)
}
verifyPercsWithAbsoluteEpsilon(t, a, s)
// If not yet flushed, results should be precise:
if !s.flushed() {
for φ, want := range map[float64]float64{
0.01: 1,
0.10: 1,
0.50: 3,
0.90: 5,
0.99: 5,
} {
if got := s.Query(φ); got != want {
t.Errorf("want %f for φ=%f, got %f", want, φ, got)
}
}
}
}
func TestLowBiasedQuery(t *testing.T) {
rand.Seed(42)
s := NewLowBiased(RelativeEpsilon)
a := populateStream(s)
verifyLowPercsWithRelativeEpsilon(t, a, s)
}
func TestHighBiasedQuery(t *testing.T) {
rand.Seed(42)
s := NewHighBiased(RelativeEpsilon)
a := populateStream(s)
verifyHighPercsWithRelativeEpsilon(t, a, s)
}
// BrokenTestTargetedMerge is broken, see Merge doc comment.
func BrokenTestTargetedMerge(t *testing.T) {
rand.Seed(42)
s1 := NewTargeted(Targets)
s2 := NewTargeted(Targets)
a := populateStream(s1)
a = append(a, populateStream(s2)...)
s1.Merge(s2.Samples())
verifyPercsWithAbsoluteEpsilon(t, a, s1)
}
// BrokenTestLowBiasedMerge is broken, see Merge doc comment.
func BrokenTestLowBiasedMerge(t *testing.T) {
rand.Seed(42)
s1 := NewLowBiased(RelativeEpsilon)
s2 := NewLowBiased(RelativeEpsilon)
a := populateStream(s1)
a = append(a, populateStream(s2)...)
s1.Merge(s2.Samples())
verifyLowPercsWithRelativeEpsilon(t, a, s2)
}
// BrokenTestHighBiasedMerge is broken, see Merge doc comment.
func BrokenTestHighBiasedMerge(t *testing.T) {
rand.Seed(42)
s1 := NewHighBiased(RelativeEpsilon)
s2 := NewHighBiased(RelativeEpsilon)
a := populateStream(s1)
a = append(a, populateStream(s2)...)
s1.Merge(s2.Samples())
verifyHighPercsWithRelativeEpsilon(t, a, s2)
}
func TestUncompressed(t *testing.T) {
q := NewTargeted(Targets)
for i := 100; i > 0; i-- {
q.Insert(float64(i))
}
if g := q.Count(); g != 100 {
t.Errorf("want count 100, got %d", g)
}
// Before compression, Query should have 100% accuracy.
for quantile := range Targets {
w := quantile * 100
if g := q.Query(quantile); g != w {
t.Errorf("want %f, got %f", w, g)
}
}
}
func TestUncompressedSamples(t *testing.T) {
q := NewTargeted(map[float64]float64{0.99: 0.001})
for i := 1; i <= 100; i++ {
q.Insert(float64(i))
}
if g := q.Samples().Len(); g != 100 {
t.Errorf("want count 100, got %d", g)
}
}
func TestUncompressedOne(t *testing.T) {
q := NewTargeted(map[float64]float64{0.99: 0.01})
q.Insert(3.14)
if g := q.Query(0.90); g != 3.14 {
t.Error("want PI, got", g)
}
}
func TestDefaults(t *testing.T) {
if g := NewTargeted(map[float64]float64{0.99: 0.001}).Query(0.99); g != 0 {
t.Errorf("want 0, got %f", g)
}
}

View File

@@ -1,90 +0,0 @@
package topk
import (
"sort"
)
// http://www.cs.ucsb.edu/research/tech_reports/reports/2005-23.pdf
type Element struct {
Value string
Count int
}
type Samples []*Element
func (sm Samples) Len() int {
return len(sm)
}
func (sm Samples) Less(i, j int) bool {
return sm[i].Count < sm[j].Count
}
func (sm Samples) Swap(i, j int) {
sm[i], sm[j] = sm[j], sm[i]
}
type Stream struct {
k int
mon map[string]*Element
// the minimum Element
min *Element
}
func New(k int) *Stream {
s := new(Stream)
s.k = k
s.mon = make(map[string]*Element)
s.min = &Element{}
// Track k+1 so that less frequenet items contended for that spot,
// resulting in k being more accurate.
return s
}
func (s *Stream) Insert(x string) {
s.insert(&Element{x, 1})
}
func (s *Stream) Merge(sm Samples) {
for _, e := range sm {
s.insert(e)
}
}
func (s *Stream) insert(in *Element) {
e := s.mon[in.Value]
if e != nil {
e.Count++
} else {
if len(s.mon) < s.k+1 {
e = &Element{in.Value, in.Count}
s.mon[in.Value] = e
} else {
e = s.min
delete(s.mon, e.Value)
e.Value = in.Value
e.Count += in.Count
s.min = e
}
}
if e.Count < s.min.Count {
s.min = e
}
}
func (s *Stream) Query() Samples {
var sm Samples
for _, e := range s.mon {
sm = append(sm, e)
}
sort.Sort(sort.Reverse(sm))
if len(sm) < s.k {
return sm
}
return sm[:s.k]
}

View File

@@ -1,57 +0,0 @@
package topk
import (
"fmt"
"math/rand"
"sort"
"testing"
)
func TestTopK(t *testing.T) {
stream := New(10)
ss := []*Stream{New(10), New(10), New(10)}
m := make(map[string]int)
for _, s := range ss {
for i := 0; i < 1e6; i++ {
v := fmt.Sprintf("%x", int8(rand.ExpFloat64()))
s.Insert(v)
m[v]++
}
stream.Merge(s.Query())
}
var sm Samples
for x, s := range m {
sm = append(sm, &Element{x, s})
}
sort.Sort(sort.Reverse(sm))
g := stream.Query()
if len(g) != 10 {
t.Fatalf("got %d, want 10", len(g))
}
for i, e := range g {
if sm[i].Value != e.Value {
t.Errorf("at %d: want %q, got %q", i, sm[i].Value, e.Value)
}
}
}
func TestQuery(t *testing.T) {
queryTests := []struct {
value string
expected int
}{
{"a", 1},
{"b", 2},
{"c", 2},
}
stream := New(2)
for _, tt := range queryTests {
stream.Insert(tt.value)
if n := len(stream.Query()); n != tt.expected {
t.Errorf("want %d, got %d", tt.expected, n)
}
}
}

8
vendor/github.com/cespare/xxhash/v2/.travis.yml generated vendored Normal file
View File

@@ -0,0 +1,8 @@
language: go
go:
- "1.x"
- master
env:
- TAGS=""
- TAGS="-tags purego"
script: go test $TAGS -v ./...

22
vendor/github.com/cespare/xxhash/v2/LICENSE.txt generated vendored Normal file
View File

@@ -0,0 +1,22 @@
Copyright (c) 2016 Caleb Spare
MIT License
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Some files were not shown because too many files have changed in this diff Show More