first commit
This commit is contained in:
5
.contrast-scan.json
Normal file
5
.contrast-scan.json
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"excludes": [
|
||||||
|
"**/vendor/**"
|
||||||
|
]
|
||||||
|
}
|
||||||
3
.gitattributes
vendored
Normal file
3
.gitattributes
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Fore LF line endings to prevent errors in Bash on Windows
|
||||||
|
* text=auto
|
||||||
|
*.sh text eol=lf
|
||||||
24
.gitignore
vendored
Normal file
24
.gitignore
vendored
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
.dockerignore
|
||||||
|
.DS_Store
|
||||||
|
.vscode
|
||||||
|
test/docker/coverage
|
||||||
|
test/docker/vendor
|
||||||
|
test/kubernetes/vendor
|
||||||
|
test/messaging/target
|
||||||
|
build
|
||||||
|
coverage
|
||||||
|
downloads
|
||||||
|
incubating/mqipt/*-IBM-MQIPT-*.tar*
|
||||||
|
vendor/github.com/prometheus/client_model/bin/
|
||||||
|
vendor/github.com/prometheus/client_model/.classpath
|
||||||
|
vendor/github.com/prometheus/client_model/.project
|
||||||
|
vendor/github.com/prometheus/client_model/.settings*
|
||||||
|
gosec_results.json
|
||||||
|
internal/qmgrauth/qmgroam/patch
|
||||||
|
.tagcache
|
||||||
|
|
||||||
|
# Nix
|
||||||
|
*.nix
|
||||||
|
.envrc
|
||||||
|
.direnv
|
||||||
|
result
|
||||||
184
.travis.yml
Normal file
184
.travis.yml
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
# © Copyright IBM Corporation 2018, 2024
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
dist: bionic
|
||||||
|
group: beta
|
||||||
|
sudo: required
|
||||||
|
language: go
|
||||||
|
|
||||||
|
go:
|
||||||
|
- "1.21.13"
|
||||||
|
|
||||||
|
services:
|
||||||
|
- docker
|
||||||
|
|
||||||
|
env:
|
||||||
|
global:
|
||||||
|
- MAIN_BRANCH=v9.4.1
|
||||||
|
- TAGCACHE_FILE=tagcache
|
||||||
|
- RELEASE=r1
|
||||||
|
|
||||||
|
go_import_path: "github.com/ibm-messaging/mq-container"
|
||||||
|
|
||||||
|
# cache:
|
||||||
|
# directories:
|
||||||
|
# - downloads
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
allow_failures:
|
||||||
|
- script: bash -e travis-build-scripts/trigger-release-checks.sh
|
||||||
|
include:
|
||||||
|
- stage: basic-build
|
||||||
|
if: branch != v9.4.1 AND tag IS blank AND branch !~ /^ifix-/
|
||||||
|
name: "Basic AMD64 build"
|
||||||
|
os: linux
|
||||||
|
env:
|
||||||
|
- MQ_ARCHIVE_REPOSITORY_DEV=$MQ_9_4_1_ARCHIVE_REPOSITORY_DEV_AMD64
|
||||||
|
script: bash -e travis-build-scripts/run.sh
|
||||||
|
|
||||||
|
# CD Build
|
||||||
|
|
||||||
|
- stage: global-tag
|
||||||
|
if: (branch = v9.4.1 OR branch =~ ^ifix-*) 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.4.1 OR tag =~ ^release-candidate*
|
||||||
|
name: "Multi-Arch AMD64 build"
|
||||||
|
os: linux
|
||||||
|
env:
|
||||||
|
- BUILD_ALL=true
|
||||||
|
- MQ_ARCHIVE_REPOSITORY=$MQ_9_4_1_ARCHIVE_REPOSITORY_AMD64
|
||||||
|
- MQ_ARCHIVE_REPOSITORY_DEV=$MQ_9_4_1_ARCHIVE_REPOSITORY_DEV_AMD64
|
||||||
|
script:
|
||||||
|
- bash travis-build-scripts/travis-log-keepalive.sh &
|
||||||
|
- bash -e travis-build-scripts/run.sh
|
||||||
|
- stage: build
|
||||||
|
if: branch = v9.4.1 OR tag =~ ^release-candidate*
|
||||||
|
name: "Multi-Arch S390X build"
|
||||||
|
os: linux
|
||||||
|
arch: s390x
|
||||||
|
group: vms390
|
||||||
|
env:
|
||||||
|
- BUILD_ALL=true
|
||||||
|
- TEST_OPTS_DOCKER="-run TestGoldenPathWithMetrics"
|
||||||
|
- MQ_ARCHIVE_REPOSITORY=$MQ_9_4_1_ARCHIVE_REPOSITORY_S390X
|
||||||
|
- MQ_ARCHIVE_REPOSITORY_DEV=$MQ_9_4_1_ARCHIVE_REPOSITORY_DEV_S390X
|
||||||
|
script:
|
||||||
|
- bash travis-build-scripts/travis-log-keepalive.sh &
|
||||||
|
- bash -e travis-build-scripts/run.sh
|
||||||
|
- stage: build
|
||||||
|
if: branch = v9.4.1 OR tag =~ ^release-candidate*
|
||||||
|
name: "Multi-Arch PPC64LE build"
|
||||||
|
os: linux
|
||||||
|
arch: ppc64le
|
||||||
|
group: power-focal
|
||||||
|
env:
|
||||||
|
- BUILD_ALL=true
|
||||||
|
- TEST_OPTS_DOCKER="-run TestGoldenPathWithMetrics"
|
||||||
|
- MQ_ARCHIVE_REPOSITORY=$MQ_9_4_1_ARCHIVE_REPOSITORY_PPC64LE
|
||||||
|
- MQ_ARCHIVE_REPOSITORY_DEV=$MQ_9_4_1_ARCHIVE_REPOSITORY_DEV_PPC64LE
|
||||||
|
script:
|
||||||
|
- bash travis-build-scripts/travis-log-keepalive.sh &
|
||||||
|
- bash -e travis-build-scripts/run.sh
|
||||||
|
- stage: push-manifest
|
||||||
|
if: branch = v9.4.1 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
|
||||||
|
- stage: trigger-release-checks
|
||||||
|
if: branch = v9.4.1 AND type != pull_request OR tag =~ ^release-candidate*
|
||||||
|
name: "Trigger release-checks build"
|
||||||
|
script: bash -e travis-build-scripts/trigger-release-checks.sh
|
||||||
|
|
||||||
|
# ifix build started
|
||||||
|
- stage: Check-upload-ifix-driver
|
||||||
|
if: branch =~ ^ifix-
|
||||||
|
name: "Check and upload ifix driver"
|
||||||
|
os: linux
|
||||||
|
script:
|
||||||
|
- bash travis-build-scripts/travis-log-keepalive.sh &
|
||||||
|
- bash -e travis-build-scripts/ifix-base-mq-driver-uploader.sh
|
||||||
|
- stage: build-ifix
|
||||||
|
if: branch =~ ^ifix-
|
||||||
|
name: "Multi-Arch AMD64 build for ifix"
|
||||||
|
os: linux
|
||||||
|
env:
|
||||||
|
- BUILD_ALL=true
|
||||||
|
- MQ_ARCHIVE_REPOSITORY=${IFIX_BASE_MQ_DRIVER_ARCHIVE_REPOSITORY}/${MQ_SNAPSHOT_NAME}/IBM_MQ_ADVANCED_${MQ_VERSION}_AMD64.tar.gz
|
||||||
|
- MQ_ARCHIVE_REPOSITORY_DEV=${IFIX_BASE_MQ_DRIVER_ARCHIVE_REPOSITORY}/${MQ_SNAPSHOT_NAME}/IBM_MQ_ADVANCED_DEV_${MQ_VERSION}_AMD64.tar.gz
|
||||||
|
script:
|
||||||
|
- bash travis-build-scripts/travis-log-keepalive.sh &
|
||||||
|
- bash -e travis-build-scripts/run.sh
|
||||||
|
- stage: build-ifix
|
||||||
|
if: branch =~ ^ifix-
|
||||||
|
name: "Multi-Arch S390X build for fix"
|
||||||
|
os: linux
|
||||||
|
arch: s390x
|
||||||
|
group: vms390
|
||||||
|
env:
|
||||||
|
- BUILD_ALL=true
|
||||||
|
- TEST_OPTS_DOCKER="-run TestGoldenPathWithMetrics"
|
||||||
|
- MQ_ARCHIVE_REPOSITORY=${IFIX_BASE_MQ_DRIVER_ARCHIVE_REPOSITORY}/${MQ_SNAPSHOT_NAME}/IBM_MQ_ADVANCED_${MQ_VERSION}_S390X.tar.gz
|
||||||
|
- MQ_ARCHIVE_REPOSITORY_DEV=${IFIX_BASE_MQ_DRIVER_ARCHIVE_REPOSITORY}/${MQ_SNAPSHOT_NAME}/IBM_MQ_ADVANCED_DEV_${MQ_VERSION}_S390X.tar.gz
|
||||||
|
script:
|
||||||
|
- bash travis-build-scripts/travis-log-keepalive.sh &
|
||||||
|
- bash -e travis-build-scripts/run.sh
|
||||||
|
- stage: build-ifix
|
||||||
|
if: branch =~ ^ifix-
|
||||||
|
name: "Multi-Arch PPC64LE build for ifix"
|
||||||
|
os: linux
|
||||||
|
arch: ppc64le
|
||||||
|
group: power-focal
|
||||||
|
env:
|
||||||
|
- BUILD_ALL=true
|
||||||
|
- TEST_OPTS_DOCKER="-run TestGoldenPathWithMetrics"
|
||||||
|
- MQ_ARCHIVE_REPOSITORY=${IFIX_BASE_MQ_DRIVER_ARCHIVE_REPOSITORY}/${MQ_SNAPSHOT_NAME}/IBM_MQ_ADVANCED_${MQ_VERSION}_PPCLE.tar.gz
|
||||||
|
- MQ_ARCHIVE_REPOSITORY_DEV=${IFIX_BASE_MQ_DRIVER_ARCHIVE_REPOSITORY}/${MQ_SNAPSHOT_NAME}/IBM_MQ_ADVANCED_DEV_${MQ_VERSION}_PPCLE.tar.gz
|
||||||
|
script:
|
||||||
|
- bash travis-build-scripts/travis-log-keepalive.sh &
|
||||||
|
- bash -e travis-build-scripts/run.sh
|
||||||
|
- stage: push-manifest-ifix
|
||||||
|
if: branch =~ ^ifix-*
|
||||||
|
name: "Push Manifest-list to registry"
|
||||||
|
env:
|
||||||
|
- PUSH_MANIFEST_ONLY=true
|
||||||
|
- BUILD_MANIFEST=true
|
||||||
|
script: bash -e travis-build-scripts/run.sh
|
||||||
|
- stage: build-manifest-ifix
|
||||||
|
if: branch =~ ^ifix-
|
||||||
|
name: "Generate build manifest file"
|
||||||
|
env:
|
||||||
|
- BUILD_MANIFEST=true
|
||||||
|
script: bash -e travis-build-scripts/run.sh
|
||||||
|
- stage: Sync-build-manifest-ifix
|
||||||
|
if: branch =~ ^ifix-
|
||||||
|
name: "Sync build manifest with stage branch"
|
||||||
|
os: linux
|
||||||
|
script:
|
||||||
|
- bash -e travis-build-scripts/manifest-sync.sh
|
||||||
|
|
||||||
|
before_install:
|
||||||
|
- make install-build-deps
|
||||||
|
- make install-credential-helper
|
||||||
|
|
||||||
|
install:
|
||||||
|
- echo nothing
|
||||||
|
|
||||||
|
before_script: echo nothing
|
||||||
|
|
||||||
|
after_success:
|
||||||
|
- make lint
|
||||||
13
.whitesource
Normal file
13
.whitesource
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"settingsInheritedFrom": "whitesource-config/whitesource-config@master",
|
||||||
|
"scanSettings": {
|
||||||
|
"configMode": "LOCAL",
|
||||||
|
"baseBranches": [
|
||||||
|
"private-master",
|
||||||
|
"v9.4.1"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"issueSettings": {
|
||||||
|
"issueRepoName": "whitesource-scan-issues"
|
||||||
|
}
|
||||||
|
}
|
||||||
214
CHANGELOG.md
Normal file
214
CHANGELOG.md
Normal file
@@ -0,0 +1,214 @@
|
|||||||
|
# Change log
|
||||||
|
|
||||||
|
## 9.4.1.0 (2024-10)
|
||||||
|
|
||||||
|
* Updated to MQ version 9.4.1.0
|
||||||
|
* Fix to diable FIPS mode for `runmqakm` key store generation, when FIPS is not enabled
|
||||||
|
* Fix APAR IT46430
|
||||||
|
* Changed build Dockerfile to reduce file duplication across image layers
|
||||||
|
* Changed shutdown flow to continue reaping orphan processes during queue manager shutdown
|
||||||
|
* Allow Native HA configuration to be externally provided rather than generated from template.
|
||||||
|
* Deprecate use of environment variable configuration of Native HA (except `MQ_NATIVE_HA=true` which is still required).
|
||||||
|
* Clarify behaviour of now deprecated environment variable configuration in IBM documentation
|
||||||
|
* Clarified new minimum versions of Docker and Podman; new version required due to the move to UBI 9
|
||||||
|
|
||||||
|
## 9.4.0.0 (2024-06)
|
||||||
|
|
||||||
|
* Updated to MQ version 9.4.0.0
|
||||||
|
* Based on [Red Hat Universal Base Image 9.4-949.1716471857](https://catalog.redhat.com/software/containers/ubi9/ubi-minimal/615bd9b4075b022acc111bf5?image=664f4c2d9cbb931e839f138b&architecture=amd64).
|
||||||
|
* **Note** UBI 9 has pending FIPS 140-3 certification. UBI 9 is not supported on the POWER 8 architecture.
|
||||||
|
* Added new optional value "mqsc" for the environment variable MQ_LOGGING_CONSOLE_SOURCE. This will reflect the contents of autocfgmqsc.LOG.
|
||||||
|
* Environment variables **MQ_ADMIN_PASSWORD** and **MQ_APP_PASSWORD** are deprecated for the MQ Advanced for Developers image. Secrets should be used to set the passwords for **app** and **admin** users.
|
||||||
|
* MQ Advanced for Developers image will no longer use mq.htpasswd file for the MQ Authorization Service. Secrets will be used to authorize the **app** and **admin** users.
|
||||||
|
* `chkmqstarted` command updated for Native-HA deployments to additionally check if the queue manager instance is in-sync with one or more replicas.
|
||||||
|
* New Model Queue `DEV.APP.MODEL.QUEUE` defined for MQ Advanced for Developers image with `BROWSE, DISPLAY, GET, INQUIRE, PUT` Authority for user `app`.
|
||||||
|
|
||||||
|
## 9.3.5.0 (2024-02)
|
||||||
|
|
||||||
|
* Updated to MQ version 9.3.5.0
|
||||||
|
|
||||||
|
### Security Fixes
|
||||||
|
* Fixed a security issue, where unencrypted credentials in mqwebuser.xml would be copied to /var/mqm. A symbolic link is now used instead.
|
||||||
|
* golang.org/x/crypto library has been upgraded to remediate CVE-2023-48795 vulnerability.
|
||||||
|
* More secure sha512 algorithm will be used instead of sha256 to create self signed Certificate in the Web keystore.
|
||||||
|
* The MQ container generates a PKCS#12 key store for use with the MQ web server.This keystore is generated using a legacy SHA-1 encryption,container code has been updated to use Pkcs12.Modern.Encode function which uses SHA-2 encryption.
|
||||||
|
* Vulnerability has been reported on PathTraversal method usages which now have been fixed.
|
||||||
|
|
||||||
|
## 9.3.4.0 (2023-12)
|
||||||
|
|
||||||
|
* Updated to MQ version 9.3.4.0
|
||||||
|
* Fixed the signal handler so that it correctly processes control signals if a termination signal is received before startup is complete
|
||||||
|
* The default value for the environment variable MQ_LOGGING_CONSOLE_SOURCE is now "qmgr,web" instead of "qmgr".
|
||||||
|
* Removed MQ Explorer and MQ SDK samples from the "incubating" folder. IBM MQ Explorer was removed from the IBM MQ install package in 9.3.0.
|
||||||
|
|
||||||
|
## 9.3.3.2-r1 (2023-10)
|
||||||
|
|
||||||
|
* Updated to MQ version 9.3.3.0
|
||||||
|
|
||||||
|
## 9.3.3.1-r1 (2023-08)
|
||||||
|
|
||||||
|
* Updated to MQ version 9.3.3.0
|
||||||
|
|
||||||
|
## 9.3.3.0-r2 (2023-07)
|
||||||
|
|
||||||
|
* Updated to MQ version 9.3.3.0
|
||||||
|
|
||||||
|
## 9.3.3.0 (2023-06)
|
||||||
|
|
||||||
|
* Updated to MQ version 9.3.3.0
|
||||||
|
|
||||||
|
## 9.3.2.0 (2023-02)
|
||||||
|
|
||||||
|
* Updated to MQ version 9.3.2.0
|
||||||
|
* Queue manager certificates with the same Subject Distinguished Name (DN) as the issuer (CA) certificate are not supported. A certificate must have a unique Subject Distinguished Name.
|
||||||
|
* New logging environment variables: MQ_LOGGING_CONSOLE_SOURCE, MQ_LOGGING_CONSOLE_FORMAT, MQ_LOGGING_CONSOLE_EXCLUDE_ID. The LOG_FORMAT variable is deprecated.
|
||||||
|
* New environment variable: MQ_QMGR_LOG_FILE_PAGES
|
||||||
|
|
||||||
|
## 9.3.1.0-r2 (2022-11)
|
||||||
|
|
||||||
|
* Queue manager attribute SSLKEYR is now set to blank instead of '/run/runmqserver/tls/key' if key and certificate are not supplied.
|
||||||
|
|
||||||
|
## 9.3.1.0 (2022-10)
|
||||||
|
|
||||||
|
* Updated to MQ version 9.3.1.0
|
||||||
|
|
||||||
|
## 9.3.0.0 (2022-06)
|
||||||
|
|
||||||
|
* Updated to MQ version 9.3.0.0
|
||||||
|
* Use `registry.access.redhat.com` instead of `registry.redhat.io`, so that you don't need to login with a Red Hat account.
|
||||||
|
* Updated default developer config to use TLS cipher `ANY_TLS12_OR_HIGHER` instead of `ANY_TLS12`
|
||||||
|
* Added default `jvm.options` file fix issue with missing preferences file causing an error in the web server log.
|
||||||
|
* Updated to allow building image from Podman on macOS (requires Podman 4.1)
|
||||||
|
* Container builds are now faster
|
||||||
|
* Updated signal handling to use a buffer, as recommended by the Go 1.17 vetting tool
|
||||||
|
|
||||||
|
## 9.2.5.0 (2022-03)
|
||||||
|
|
||||||
|
* Updated to MQ version 9.2.5.0
|
||||||
|
|
||||||
|
## 9.2.4.0 (2021-11)
|
||||||
|
|
||||||
|
* Updated to MQ version 9.2.4.0
|
||||||
|
|
||||||
|
## 9.2.3.0 (2021-07-22)
|
||||||
|
|
||||||
|
* Updated to MQ version 9.2.3.0
|
||||||
|
|
||||||
|
## 9.2.2.0 (2021-03-26)
|
||||||
|
|
||||||
|
* Updated to MQ version 9.2.2.0
|
||||||
|
|
||||||
|
## 9.2.1.0 (2020-02-18)
|
||||||
|
|
||||||
|
* Updated to MQ version 9.2.1.0
|
||||||
|
|
||||||
|
|
||||||
|
## 9.2.0.1-LTS (2020-12-04)
|
||||||
|
|
||||||
|
* Added support for MQ Long Term Support (production licensed only) in the mq-container
|
||||||
|
|
||||||
|
## 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)
|
||||||
|
* Use `-ic` arguments with `crtmqm` to process MQSC files in `/etc/mqm`. Replaces previous use of "runmqsc" commands
|
||||||
|
|
||||||
|
## 9.1.5.0 (2020-04-02)
|
||||||
|
|
||||||
|
* Updated to MQ version 9.1.5.0
|
||||||
|
* Can now run as a random user, instead of the "mqm" user, which has now been removed. This adds compatability for the [Red Hat OpenShift restricted SCC](https://docs.openshift.com/container-platform/4.3/authentication/managing-security-context-constraints.html#security-context-constraints-about_configuring-internal-oauth). The default image UID is `1001`.
|
||||||
|
|
||||||
|
## 9.1.4.0 (2019-12-06)
|
||||||
|
|
||||||
|
* Updated to MQ version 9.1.4.0
|
||||||
|
* Updated to use UBI8 as base image
|
||||||
|
* Added required security settings to self signed certificates to align with macOS Catalina requirements
|
||||||
|
|
||||||
|
## 9.1.3.0 (2019-07-19)
|
||||||
|
|
||||||
|
* Updated to MQ version 9.1.3.0
|
||||||
|
* Allow generation of TLS certificate with given hostname
|
||||||
|
* Fixes for the following issues:
|
||||||
|
* `MQ_EPHEMERAL_PREFIX` UNIX sockets fix
|
||||||
|
* Fix Makefile for Windows
|
||||||
|
* Use -a option on crtmqdir
|
||||||
|
* Remove check for certificate environment variable
|
||||||
|
|
||||||
|
## 9.1.2.0-UBI (2019-06-21)
|
||||||
|
|
||||||
|
**Breaking changes**:
|
||||||
|
* UID of the mqm user is now 888. You need to run the container with an entrypoint of `runmqserver -i` under the root user to update any existing files.
|
||||||
|
* MQSC files supplied will be verified before being run. Files containing invalid MQSC will cause the container to fail to start
|
||||||
|
|
||||||
|
**Other changes**:
|
||||||
|
* Security fixes
|
||||||
|
* Web console added to production image
|
||||||
|
* Container built on RedHat host
|
||||||
|
|
||||||
|
## 9.1.2.0 (2019-03-21)
|
||||||
|
|
||||||
|
* Updated to MQ version 9.1.2.0
|
||||||
|
* Now runs using the "mqm" user instead of root. See new [security doc](https://github.com/ibm-messaging/mq-container/blob/master/docs/security.md)
|
||||||
|
* New [IGNSTATE](https://www.ibm.com/support/knowledgecenter/en/SSFKSJ_9.1.0/com.ibm.mq.pro.doc/q132310_.htm#q132310___ignstateparm) parameter used in default developer config
|
||||||
|
* Termination log moved from `/dev/termination-log` to `/run/termination-log`, to make permissions easier to handle
|
||||||
|
* Fixes for the following issues:
|
||||||
|
* Brackets no longer appear in termination log
|
||||||
|
* Test timeouts weren't being used correctly
|
||||||
|
* Building on subscribed and unsubscribed hosts ([#273](https://github.com/ibm-messaging/mq-container/pull/273))
|
||||||
|
* Gosec failures ([#286](https://github.com/ibm-messaging/mq-container/pull/286))
|
||||||
|
* Security fix for perl-base ([#253](https://github.com/ibm-messaging/mq-container/pull/253))
|
||||||
|
|
||||||
|
## 9.1.1.0 (2018-11-30)
|
||||||
|
|
||||||
|
* Updated to MQ version 9.1.1.0
|
||||||
|
* Created seperate RedHat Makefile for building images on RedHat machines with buildah
|
||||||
|
* Enabled REST messaging capability for app user.
|
||||||
|
* Added support for container supplementary groups
|
||||||
|
* Removed IBM MQ version 9.0.5 details.
|
||||||
|
* Added additional Diagnostics ([#203](https://github.com/ibm-messaging/mq-container/pull/203))
|
||||||
|
* Implementted GOSec to perform code scans for security vulnerabilities. (([#227](https://github.com/ibm-messaging/mq-container/pull/227)))
|
||||||
|
* Removed Queue manager create option from the MQ Console.
|
||||||
|
* Fixes for the following issues:
|
||||||
|
* Check explicitly for `/mnt/mqm` ([#175](https://github.com/ibm-messaging/mq-container/pull/175))
|
||||||
|
* Force string output in chkmqhealthy ([#174](https://github.com/ibm-messaging/mq-container/pull/174))
|
||||||
|
* Use -aG not -G when adding a group for a user
|
||||||
|
* Security fixes for libsystemd0 systemd systemd-sysv & libudev1
|
||||||
|
|
||||||
|
## 9.1.0.0 (2018-07-23)
|
||||||
|
|
||||||
|
* Updated to MQ version 9.1.0.0
|
||||||
|
* Added Docker 1.12 tests
|
||||||
|
* Added MQ SDK Docker image sample
|
||||||
|
* Added MQ Golang SDK Docker image sample
|
||||||
|
* Added Prometheus metric gathering implementation
|
||||||
|
* Added MQ Internet Pass-Thru (MS81) Docker image sample
|
||||||
|
* Added POWER & z/Linux image builds
|
||||||
|
* `devjmstest` image now built with Maven instead of gradle
|
||||||
|
* Added FAT manifests for Docker Hub/Docker Store
|
||||||
|
* Added Red Hat Enterprise Linux image build
|
||||||
|
* Added basic versioning debug information into golang programs
|
||||||
|
* Removed 9.0.4
|
||||||
|
|
||||||
|
## 9.0.5.0 (2018-03-13)
|
||||||
|
|
||||||
|
* Updated to MQ version 9.0.5.0
|
||||||
|
* Container's stdout can now be set to JSON format (set LOG_FORMAT=json)
|
||||||
|
* MQ error logs (in JSON or plain text) are now mirrored on stdout for the container.
|
||||||
|
* `chkmqready` now waits until MQSC scripts in `/etc/mqm` have been applied
|
||||||
|
* `chkmqready` and `chkmqhealthy` now run as the "mqm" user
|
||||||
|
* Added ability to optionally use an alternative base image
|
||||||
|
* Various build and test improvements
|
||||||
|
* Removed 9.0.3
|
||||||
|
|
||||||
|
## 9.0.4 (2017-11-06)
|
||||||
|
|
||||||
|
* Updated to MQ version 9.0.4.0
|
||||||
|
* Updated to Go version 9
|
||||||
|
* Removed packages `curl`, `ca-certificates`, and their dependencies, which were only used at build time
|
||||||
|
* Improved logging
|
||||||
|
* Helm charts now work on Kubernetes V1.6
|
||||||
|
* Production Helm chart now includes a default image repository and tag
|
||||||
|
* Updated to use multi-stage Docker build, so that Go code is built inside a container
|
||||||
|
|
||||||
|
## 9.0.3 (2017-10-17)
|
||||||
|
|
||||||
|
* Initial version
|
||||||
32
CLA.md
Normal file
32
CLA.md
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
IBM Contributor License Agreement
|
||||||
|
=================================
|
||||||
|
|
||||||
|
Version 1.0.0 January 14, 2014
|
||||||
|
|
||||||
|
In order for You (as defined below) to make intellectual property Contributions (as defined below) now or in the future to IBM GitHub repositories,
|
||||||
|
You must agree to this Contributor License Agreement ("CLA").
|
||||||
|
|
||||||
|
Please read this CLA carefully before accepting its terms. By accepting the CLA, You are agreeing to be bound by its terms.
|
||||||
|
If You submit a Pull Request against an IBM repository on GitHub You must include in the Pull Request a statement of Your acceptance of this CLA.
|
||||||
|
|
||||||
|
As used in this CLA:
|
||||||
|
(i) "You" (or "Your") shall mean the entity that is making this Agreement with IBM;
|
||||||
|
(ii)"Contribution" shall mean any original work of authorship, including any modifications or additions to an existing work, that is submitted by You to IBM for inclusion in,
|
||||||
|
or documentation of, any of the IBM GitHub repositories;
|
||||||
|
(iii) "Submit" (or "Submitted") means any form of communication sent to IBM (e.g. the content You post in a GitHub Issue or submit as part of a GitHub Pull Request).
|
||||||
|
|
||||||
|
This agreement applies to all Contributions You Submit.
|
||||||
|
|
||||||
|
This CLA, and the license(s) associated with the particular IBM GitHub repositories You are contributing to, provides a license to Your Contributions to IBM and downstream consumers,
|
||||||
|
but You still own Your Contributions, and except for the licenses provided for in this CLA, You reserve all right, title and interest in Your Contributions.
|
||||||
|
|
||||||
|
IBM requires that each Contribution You Submit now or in the future comply with the following four commitments.
|
||||||
|
|
||||||
|
1) You will only Submit Contributions where You have authored 100% of the content.
|
||||||
|
2) You will only Submit Contributions to which You have the necessary rights. This means that if You are employed You have received the necessary permissions from Your employer to make the
|
||||||
|
Contributions.
|
||||||
|
3) Whatever content You Contribute will be provided under the license(s) associated with the particular IBM GitHub repository You are contributing to.
|
||||||
|
4) You understand and agree that IBM GitHub repositories and Your contributions are public, and that a record of the contribution (including all personal information You submit with it)
|
||||||
|
is maintained indefinitely and may be redistributed consistent with the license(s) involved.
|
||||||
|
You will promptly notify the Eclipse Foundation if You become aware of any facts or circumstances that would make these commitments inaccurate in any way.
|
||||||
|
To do so, please create an Issue in the appropriate GitHub repository.
|
||||||
203
Dockerfile-server
Normal file
203
Dockerfile-server
Normal file
@@ -0,0 +1,203 @@
|
|||||||
|
# © Copyright IBM Corporation 2015, 2024
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
ARG BASE_IMAGE=registry.access.redhat.com/ubi9/ubi-minimal
|
||||||
|
ARG BASE_TAG=9.4-1227.1726694542
|
||||||
|
ARG BUILDER_IMAGE=registry.access.redhat.com/ubi9/go-toolset
|
||||||
|
ARG BUILDER_TAG=1.21.13-2.1727893526
|
||||||
|
ARG GO_WORKDIR=/opt/app-root/src/go/src/github.com/ibm-messaging/mq-container
|
||||||
|
ARG MQ_ARCHIVE="downloads/9.4.1.0-IBM-MQ-Advanced-for-Developers-Non-Install-LinuxX64.tar.gz"
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Build stage to build Go code
|
||||||
|
###############################################################################
|
||||||
|
FROM $BUILDER_IMAGE:$BUILDER_TAG as builder
|
||||||
|
ARG IMAGE_REVISION="Not specified"
|
||||||
|
ARG IMAGE_SOURCE="Not specified"
|
||||||
|
ARG IMAGE_TAG="Not specified"
|
||||||
|
ARG GO_WORKDIR
|
||||||
|
ARG MQ_ARCHIVE
|
||||||
|
USER 0
|
||||||
|
WORKDIR $GO_WORKDIR/
|
||||||
|
ADD $MQ_ARCHIVE /opt/mqm
|
||||||
|
ENV CGO_CFLAGS="-I/opt/mqm/inc/" \
|
||||||
|
CGO_LDFLAGS_ALLOW="-Wl,-rpath.*" \
|
||||||
|
PATH="${PATH}:/opt/mqm/bin"
|
||||||
|
COPY go.mod go.sum ./
|
||||||
|
COPY cmd/ ./cmd
|
||||||
|
COPY internal/ ./internal
|
||||||
|
COPY ha/ ./ha
|
||||||
|
COPY pkg/ ./pkg
|
||||||
|
COPY vendor/ ./vendor
|
||||||
|
RUN go build -ldflags "-X \"main.ImageCreated=$(date --iso-8601=seconds)\" -X \"main.ImageRevision=$IMAGE_REVISION\" -X \"main.ImageSource=$IMAGE_SOURCE\" -X \"main.ImageTag=$IMAGE_TAG\"" ./cmd/runmqserver/ \
|
||||||
|
&& go build ./cmd/chkmqready/ \
|
||||||
|
&& go build ./cmd/chkmqhealthy/ \
|
||||||
|
&& go build ./cmd/chkmqstarted/ \
|
||||||
|
&& go build ./cmd/runmqdevserver/ \
|
||||||
|
&& chmod ug+x ./chkmq* ./runmq* \
|
||||||
|
&& go test -v ./cmd/runmqdevserver/... \
|
||||||
|
&& go test -v ./cmd/runmqserver/ \
|
||||||
|
&& go test -v ./cmd/chkmqready/ \
|
||||||
|
&& go test -v ./cmd/chkmqhealthy/ \
|
||||||
|
&& go test -v ./cmd/chkmqstarted/ \
|
||||||
|
&& go test -v ./pkg/... \
|
||||||
|
&& go test -v ./internal/... \
|
||||||
|
&& go vet ./cmd/... ./internal/...
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Build stage to reduce MQ packages included using genmqpkg
|
||||||
|
###############################################################################
|
||||||
|
FROM $BASE_IMAGE:$BASE_TAG AS mq-redux
|
||||||
|
ARG BASE_IMAGE
|
||||||
|
ARG BASE_TAG
|
||||||
|
ARG MQ_ARCHIVE
|
||||||
|
WORKDIR /tmp/mq
|
||||||
|
ENV genmqpkg_inc32=1 \
|
||||||
|
genmqpkg_incadm=1 \
|
||||||
|
genmqpkg_incamqp=1 \
|
||||||
|
genmqpkg_incams=1 \
|
||||||
|
genmqpkg_inccbl=1 \
|
||||||
|
genmqpkg_inccics=1 \
|
||||||
|
genmqpkg_inccpp=1 \
|
||||||
|
genmqpkg_incdnet=1 \
|
||||||
|
genmqpkg_incjava=1 \
|
||||||
|
genmqpkg_incjre=1 \
|
||||||
|
genmqpkg_incman=1 \
|
||||||
|
genmqpkg_incmqbc=1 \
|
||||||
|
genmqpkg_incmqft=1 \
|
||||||
|
genmqpkg_incmqsf=1 \
|
||||||
|
genmqpkg_incmqxr=1 \
|
||||||
|
genmqpkg_incnls=1 \
|
||||||
|
genmqpkg_incras=1 \
|
||||||
|
genmqpkg_incsamp=1 \
|
||||||
|
genmqpkg_incsdk=1 \
|
||||||
|
genmqpkg_inctls=1 \
|
||||||
|
genmqpkg_incunthrd=1 \
|
||||||
|
genmqpkg_incweb=1
|
||||||
|
ADD $MQ_ARCHIVE /opt/mqm-noinstall
|
||||||
|
# Run genmqpkg to reduce the MQ packages included
|
||||||
|
RUN /opt/mqm-noinstall/bin/genmqpkg.sh -b /opt/mqm-redux
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Main build stage, to build MQ image
|
||||||
|
###############################################################################
|
||||||
|
FROM $BASE_IMAGE:$BASE_TAG AS mq-server
|
||||||
|
ARG MQ_URL
|
||||||
|
ARG BASE_IMAGE
|
||||||
|
ARG BASE_TAG
|
||||||
|
ARG GO_WORKDIR
|
||||||
|
LABEL summary="IBM MQ Advanced Server" \
|
||||||
|
description="Simplify, accelerate and facilitate the reliable exchange of data with a security-rich messaging solution — trusted by the world’s most successful enterprises" \
|
||||||
|
vendor="IBM" \
|
||||||
|
maintainer="IBM" \
|
||||||
|
distribution-scope="private" \
|
||||||
|
authoritative-source-url="https://www.ibm.com/software/passportadvantage/" \
|
||||||
|
url="https://www.ibm.com/products/mq/advanced" \
|
||||||
|
io.openshift.tags="mq messaging" \
|
||||||
|
io.k8s.display-name="IBM MQ Advanced Server" \
|
||||||
|
io.k8s.description="Simplify, accelerate and facilitate the reliable exchange of data with a security-rich messaging solution — trusted by the world’s most successful enterprises" \
|
||||||
|
base-image=$BASE_IMAGE \
|
||||||
|
base-image-release=$BASE_TAG
|
||||||
|
COPY --chown=1001:root --from=mq-redux /opt/mqm-redux/ /opt/mqm/
|
||||||
|
COPY --chown=1001:root setup-image.sh /usr/local/bin/
|
||||||
|
COPY --chown=1001:root install-mq-server-prereqs.sh /usr/local/bin/
|
||||||
|
RUN env \
|
||||||
|
&& chmod u+x /usr/local/bin/install-*.sh \
|
||||||
|
&& chmod u+x /usr/local/bin/setup-image.sh \
|
||||||
|
&& install-mq-server-prereqs.sh \
|
||||||
|
&& setup-image.sh \
|
||||||
|
&& /opt/mqm/bin/security/amqpamcf
|
||||||
|
COPY --chown=1001:root --from=builder $GO_WORKDIR/runmqserver /usr/local/bin/
|
||||||
|
COPY --chown=1001:root --from=builder $GO_WORKDIR/chkmq* /usr/local/bin/
|
||||||
|
COPY --chown=1001:root ha/*.ini.tpl /etc/mqm/
|
||||||
|
# Copy web XML files
|
||||||
|
COPY --chown=1001:root web /etc/mqm/web
|
||||||
|
COPY --chown=1001:root etc/mqm/*.tpl /etc/mqm/
|
||||||
|
RUN ln -s /run/mqwebcontainer.xml /etc/mqm/web/installations/Installation1/servers/mqweb/mqwebcontainer.xml \
|
||||||
|
&& ln -s /run/tls.xml /etc/mqm/web/installations/Installation1/servers/mqweb/tls.xml \
|
||||||
|
&& ln -s /run/jvm.options /etc/mqm/web/installations/Installation1/servers/mqweb/configDropins/defaults/jvm.options \
|
||||||
|
&& ln -s /run/15-tls.mqsc /etc/mqm/15-tls.mqsc \
|
||||||
|
&& ln -s /run/10-native-ha.ini /etc/mqm/10-native-ha.ini \
|
||||||
|
&& ln -s /run/10-native-ha-instance.ini /etc/mqm/10-native-ha-instance.ini \
|
||||||
|
&& ln -s /run/10-native-ha-keystore.ini /etc/mqm/10-native-ha-keystore.ini \
|
||||||
|
&& chown -R 1001:root /etc/mqm/*
|
||||||
|
RUN touch /run/termination-log \
|
||||||
|
&& chown 1001:root /run/termination-log \
|
||||||
|
&& chmod 0660 /run/termination-log \
|
||||||
|
&& chmod -R g+w /etc/mqm/web \
|
||||||
|
&& chmod 0660 /etc/mqm/web/installations/Installation1/servers/mqweb/mqwebuser.xml
|
||||||
|
# Always use port 1414 for MQ, 9157 for the metrics, and 9415 for Native HA recovery
|
||||||
|
EXPOSE 1414 9157 9415 9443
|
||||||
|
ENV MQ_OVERRIDE_DATA_PATH=/mnt/mqm/data MQ_OVERRIDE_INSTALLATION_NAME=Installation1 MQ_USER_NAME="mqm" PATH="${PATH}:/opt/mqm/bin"
|
||||||
|
ENV MQ_GRACE_PERIOD=30
|
||||||
|
ENV LANG=C AMQ_DIAGNOSTIC_MSG_SEVERITY=1 AMQ_ADDITIONAL_JSON_LOG=1
|
||||||
|
ENV MQ_LOGGING_CONSOLE_EXCLUDE_ID=AMQ5041I,AMQ5052I,AMQ5051I,AMQ5037I,AMQ5975I
|
||||||
|
ENV WLP_LOGGING_MESSAGE_FORMAT=json
|
||||||
|
# We can run as any UID
|
||||||
|
USER 1001
|
||||||
|
ENV MQ_CONNAUTH_USE_HTP=false
|
||||||
|
ENTRYPOINT ["runmqserver"]
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Build stage to build C code for custom authorization service (developer-only)
|
||||||
|
###############################################################################
|
||||||
|
# Use the Go toolset image, which already includes gcc and the MQ SDK
|
||||||
|
FROM builder as cbuilder
|
||||||
|
USER 0
|
||||||
|
# Install the Apache Portable Runtime code (used for simpleauth hash checking)
|
||||||
|
COPY authservice/ /opt/app-root/src/authservice/
|
||||||
|
WORKDIR /opt/app-root/src/authservice/mqsimpleauth
|
||||||
|
RUN make all
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Add default developer config
|
||||||
|
###############################################################################
|
||||||
|
FROM mq-server AS mq-dev-server
|
||||||
|
ARG BASE_IMAGE
|
||||||
|
ARG BASE_TAG
|
||||||
|
ARG GO_WORKDIR
|
||||||
|
LABEL summary="IBM MQ Advanced for Developers Server" \
|
||||||
|
description="Simplify, accelerate and facilitate the reliable exchange of data with a security-rich messaging solution — trusted by the world’s most successful enterprises" \
|
||||||
|
vendor="IBM" \
|
||||||
|
distribution-scope="private" \
|
||||||
|
authoritative-source-url="https://www.ibm.com/software/passportadvantage/" \
|
||||||
|
url="https://www.ibm.com/products/mq/advanced" \
|
||||||
|
io.openshift.tags="mq messaging" \
|
||||||
|
io.k8s.display-name="IBM MQ Advanced for Developers Server" \
|
||||||
|
io.k8s.description="Simplify, accelerate and facilitate the reliable exchange of data with a security-rich messaging solution — trusted by the world’s most successful enterprises" \
|
||||||
|
base-image=$BASE_IMAGE \
|
||||||
|
base-image-release=$BASE_TAG
|
||||||
|
USER 0
|
||||||
|
COPY --from=cbuilder /opt/app-root/src/authservice/mqsimpleauth/build/mqsimpleauth.so /opt/mqm/lib64/
|
||||||
|
COPY --chown=1001:root etc/mqm/qm-service-component.ini.default /etc/mqm/
|
||||||
|
COPY --chown=1001:root --from=builder $GO_WORKDIR/runmqdevserver /usr/local/bin/
|
||||||
|
# Copy template files
|
||||||
|
COPY --chown=1001:root incubating/mqadvanced-server-dev/*.tpl /etc/mqm/
|
||||||
|
# Copy web XML files for default developer configuration
|
||||||
|
COPY --chown=1001:root incubating/mqadvanced-server-dev/web /etc/mqm/web
|
||||||
|
RUN ln -s /run/10-dev.mqsc /etc/mqm/10-dev.mqsc \
|
||||||
|
&& ln -s /run/20-dev-tls.mqsc /etc/mqm/20-dev-tls.mqsc \
|
||||||
|
&& chown --no-dereference 1001:root /etc/mqm/*.mqsc
|
||||||
|
RUN chmod -R g+w /etc/mqm/web \
|
||||||
|
&& ln -s /run/qm-service-component.ini /etc/mqm/qm-service-component.ini \
|
||||||
|
&& chown --no-dereference 1001:root /etc/mqm/qm-service-component.ini
|
||||||
|
|
||||||
|
ENV MQ_DEV=true \
|
||||||
|
MQ_ENABLE_EMBEDDED_WEB_SERVER=1 \
|
||||||
|
MQ_GENERATE_CERTIFICATE_HOSTNAME=localhost \
|
||||||
|
LD_LIBRARY_PATH=/opt/mqm/lib64 \
|
||||||
|
MQ_CONNAUTH_USE_HTP=true \
|
||||||
|
MQS_PERMIT_UNKNOWN_ID=true
|
||||||
|
USER 1001
|
||||||
|
ENTRYPOINT ["runmqdevserver"]
|
||||||
32
Dockerfile-server.cover
Normal file
32
Dockerfile-server.cover
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
# © Copyright IBM Corporation 2017, 2018
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
ARG BASE_IMAGE
|
||||||
|
|
||||||
|
# Build stage to build Go code
|
||||||
|
FROM golang:1.10 as builder
|
||||||
|
WORKDIR /go/src/github.com/ibm-messaging/mq-container/
|
||||||
|
COPY cmd/ ./cmd
|
||||||
|
COPY internal/ ./internal
|
||||||
|
COPY vendor/ ./vendor
|
||||||
|
RUN go test -c -covermode=count -coverpkg $(go list ./cmd/runmqserver ./internal/... | paste -s -d, -) ./cmd/runmqserver
|
||||||
|
|
||||||
|
FROM $BASE_IMAGE
|
||||||
|
|
||||||
|
# Copy in the version of the code instrumented for code coverage
|
||||||
|
COPY --from=builder /go/src/github.com/ibm-messaging/mq-container/runmqserver.test /usr/local/bin/
|
||||||
|
RUN chmod +x /usr/local/bin/runmqserver.test \
|
||||||
|
&& mkdir -p /var/coverage/
|
||||||
|
|
||||||
|
ENTRYPOINT ["runmqserver.test", "-test", "-test.coverprofile", "/var/coverage/container.cov"]
|
||||||
191
LICENSE
Normal file
191
LICENSE
Normal file
@@ -0,0 +1,191 @@
|
|||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
© Copyright IBM Corporation. 2015, 2019
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
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.
|
||||||
631
Makefile
Normal file
631
Makefile
Normal file
@@ -0,0 +1,631 @@
|
|||||||
|
# © Copyright IBM Corporation 2017, 2023
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Conditional variables - you can override the values of these variables from
|
||||||
|
# the command line
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
include config.env
|
||||||
|
include source-branch.env
|
||||||
|
|
||||||
|
# arch_uname is the platform architecture according to the uname program. Can be differ by OS, e.g. `arm64` on macOS, but `aarch64` on Linux.
|
||||||
|
arch_uname := $(shell uname -m)
|
||||||
|
# arch_go is the platform architecture in Go-style (e.g. amd64, ppc64le, s390x or arm64).
|
||||||
|
arch_go := $(if $(findstring x86_64,$(arch_uname)),amd64,$(if $(findstring aarch64,$(arch_uname)),arm64,$(arch_uname)))
|
||||||
|
# ARCH is the platform architecture in Go-style (e.g. amd64, ppc64le, s390x or arm64).
|
||||||
|
# Override this to build an image for a different architecture. Note that RUN instructions will not be able to succeed without the help of emulation provided by packages like qemu-user-static.
|
||||||
|
ARCH ?= $(arch_go)
|
||||||
|
# RELEASE shows what release of the container code has been built
|
||||||
|
RELEASE ?=
|
||||||
|
# MQ_ARCHIVE_REPOSITORY is a remote repository from which to pull the MQ_ARCHIVE (if required)
|
||||||
|
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 ?=
|
||||||
|
# MQ_ARCHIVE_REPOSITORY_USER is the user for the remote repository (if required)
|
||||||
|
MQ_ARCHIVE_REPOSITORY_USER ?=
|
||||||
|
# MQ_ARCHIVE_REPOSITORY_CREDENTIAL is the password/API key for the remote repository (if required)
|
||||||
|
MQ_ARCHIVE_REPOSITORY_CREDENTIAL ?=
|
||||||
|
# MQ_ARCHIVE is the name of the file, under the downloads directory, from which MQ Advanced can
|
||||||
|
# be installed. Does not apply to MQ Advanced for Developers
|
||||||
|
MQ_ARCHIVE ?= IBM_MQ_$(MQ_VERSION_VRM)_$(MQ_ARCHIVE_TYPE)_$(MQ_ARCHIVE_ARCH)_NOINST.tar.gz
|
||||||
|
# MQ_ARCHIVE_DEV is the name of the file, under the downloads directory, from which MQ Advanced
|
||||||
|
# for Developers can be installed
|
||||||
|
MQ_ARCHIVE_DEV ?= $(MQ_VERSION)-IBM-MQ-Advanced-for-Developers-Non-Install-$(MQ_ARCHIVE_DEV_TYPE)$(MQ_ARCHIVE_DEV_ARCH).tar.gz
|
||||||
|
# MQ_SDK_ARCHIVE specifies the archive to use for building the golang programs. Defaults vary on developer or advanced.
|
||||||
|
MQ_SDK_ARCHIVE ?= $(MQ_ARCHIVE_DEV_$(MQ_VERSION))
|
||||||
|
# Options to `go test` for the Container tests
|
||||||
|
TEST_OPTS_CONTAINER ?=
|
||||||
|
# Timeout for the tests
|
||||||
|
TEST_TIMEOUT_CONTAINER ?= 45m
|
||||||
|
# MQ_IMAGE_ADVANCEDSERVER is the name of the built MQ Advanced image
|
||||||
|
MQ_IMAGE_ADVANCEDSERVER ?=ibm-mqadvanced-server
|
||||||
|
# MQ_IMAGE_DEVSERVER is the name of the built MQ Advanced for Developers image
|
||||||
|
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 ?= $(MQ_MANIFEST_TAG)-$(ARCH)
|
||||||
|
# COMMAND is the container command to run. "podman" or "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 ?=
|
||||||
|
# MQ_DELIVERY_REGISTRY_NAMESPACE is the namespace/path on the delivery registry (if required)
|
||||||
|
MQ_DELIVERY_REGISTRY_NAMESPACE ?=
|
||||||
|
# MQ_DELIVERY_REGISTRY_USER is the user for the remote registry (if required)
|
||||||
|
MQ_DELIVERY_REGISTRY_USER ?=
|
||||||
|
# MQ_DELIVERY_REGISTRY_CREDENTIAL is the password/API key for the remote registry (if required)
|
||||||
|
MQ_DELIVERY_REGISTRY_CREDENTIAL ?=
|
||||||
|
# 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
|
||||||
|
###############################################################################
|
||||||
|
# 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
|
||||||
|
MQ_ARCHIVE_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=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
|
||||||
|
# BASE_IMAGE_TAG is a normalized version of BASE_IMAGE, suitable for use in a Docker tag
|
||||||
|
BASE_IMAGE_TAG=$(lastword $(subst /, ,$(subst :,-,$(BASE_IMAGE))))
|
||||||
|
#BASE_IMAGE_TAG=$(subst /,-,$(subst :,-,$(BASE_IMAGE)))
|
||||||
|
MQ_IMAGE_DEVSERVER_BASE=mqadvanced-server-dev-base
|
||||||
|
# Docker image name to use for JMS tests
|
||||||
|
DEV_JMS_IMAGE=mq-dev-jms-test
|
||||||
|
# Variables for versioning
|
||||||
|
IMAGE_REVISION=$(shell git rev-parse HEAD)
|
||||||
|
IMAGE_SOURCE=$(shell git config --get remote.origin.url)
|
||||||
|
EMPTY:=
|
||||||
|
SPACE:= $(EMPTY) $(EMPTY)
|
||||||
|
# MQ_VERSION_VRM is MQ_VERSION with only the Version, Release and Modifier fields (no Fix field). e.g. 9.2.0 instead of 9.2.0.0
|
||||||
|
MQ_VERSION_VRM=$(subst $(SPACE),.,$(wordlist 1,3,$(subst .,$(SPACE),$(MQ_VERSION))))
|
||||||
|
|
||||||
|
ifeq "$(COMMAND)" "podman"
|
||||||
|
NUM_CPU ?= $(or $(shell podman info --format "{{.Host.CPUs}}"),2)
|
||||||
|
else ifeq "$(COMMAND)" "docker"
|
||||||
|
NUM_CPU ?= $(or $(shell docker info --format "{{ .NCPU }}"),2)
|
||||||
|
else
|
||||||
|
NUM_CPU ?= 2
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifneq (,$(findstring Microsoft,$(shell uname -r)))
|
||||||
|
DOWNLOADS_DIR=$(patsubst /mnt/c%,C:%,$(realpath ./downloads/))
|
||||||
|
else ifneq (,$(findstring Windows,$(shell echo ${OS})))
|
||||||
|
DOWNLOADS_DIR=$(shell pwd)/downloads/
|
||||||
|
else
|
||||||
|
DOWNLOADS_DIR=$(realpath ./downloads/)
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Try to figure out which archive to use from the architecture
|
||||||
|
ifeq "$(ARCH)" "amd64"
|
||||||
|
MQ_ARCHIVE_ARCH:=X86-64
|
||||||
|
MQ_ARCHIVE_DEV_ARCH:=X64
|
||||||
|
else ifeq "$(ARCH)" "ppc64le"
|
||||||
|
MQ_ARCHIVE_ARCH:=PPC64LE
|
||||||
|
MQ_ARCHIVE_DEV_ARCH:=PPC64LE
|
||||||
|
else ifeq "$(ARCH)" "s390x"
|
||||||
|
MQ_ARCHIVE_ARCH:=S390X
|
||||||
|
MQ_ARCHIVE_DEV_ARCH:=S390X
|
||||||
|
else ifeq "$(ARCH)" "arm64"
|
||||||
|
MQ_ARCHIVE_ARCH:=ARM64
|
||||||
|
MQ_ARCHIVE_DEV_ARCH:=ARM64
|
||||||
|
endif
|
||||||
|
|
||||||
|
# If this is a fake master build, push images to alternative location (pipeline wont consider these images GA candidates)
|
||||||
|
ifeq ($(shell [ "$(TRAVIS)" = "true" ] && [ -n "$(MAIN_BRANCH)" ] && [ -n "$(SOURCE_BRANCH)" ] && [ "$(MAIN_BRANCH)" != "$(SOURCE_BRANCH)" ] && echo "true"), true)
|
||||||
|
MQ_DELIVERY_REGISTRY_NAMESPACE="master-fake"
|
||||||
|
endif
|
||||||
|
|
||||||
|
# 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)"
|
||||||
|
MQ_DELIVERY_REGISTRY_FULL_PATH=$(MQ_DELIVERY_REGISTRY_HOSTNAME)/$(MQ_DELIVERY_REGISTRY_NAMESPACE)
|
||||||
|
else
|
||||||
|
MQ_DELIVERY_REGISTRY_FULL_PATH=$(MQ_DELIVERY_REGISTRY_HOSTNAME)
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(shell [ ! -z $(TRAVIS) ] && echo "$(TRAVIS_BRANCH)" | grep -q '^ifix-' && echo true), true)
|
||||||
|
MQ_DELIVERY_REGISTRY_FULL_PATH=$(MQ_DELIVERY_REGISTRY_HOSTNAME)/$(MQ_DELIVERY_REGISTRY_NAMESPACE_IFIX)
|
||||||
|
MQ_DELIVERY_REGISTRY_NAMESPACE=$(MQ_DELIVERY_REGISTRY_NAMESPACE_IFIX)
|
||||||
|
endif
|
||||||
|
|
||||||
|
# image tagging
|
||||||
|
|
||||||
|
ifneq "$(RELEASE)" "$(EMPTY)"
|
||||||
|
EXTRA_LABELS_RELEASE=--label "release=$(RELEASE)"
|
||||||
|
RELEASE_TAG="-$(RELEASE)"
|
||||||
|
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
|
||||||
|
|
||||||
|
ifeq ($(shell [ ! -z $(TRAVIS) ] && [ "$(TRAVIS_PULL_REQUEST)" = "false" ] && echo "$(TRAVIS_BRANCH)" | grep -q '^ifix-' && echo true), true)
|
||||||
|
MQ_MANIFEST_TAG_SUFFIX=.$(TIMESTAMPFLAT).$(GIT_COMMIT)
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Make sure we don't use VOLUME_MOUNT_OPTIONS for Podman on macOS
|
||||||
|
ifeq "$(COMMAND)" "podman"
|
||||||
|
ifeq "$(shell uname -s)" "Darwin"
|
||||||
|
VOLUME_MOUNT_OPTIONS:=
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
PATH_TO_MQ_TAG_CACHE=$(TRAVIS_BUILD_DIR)/.tagcache
|
||||||
|
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_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)
|
||||||
|
|
||||||
|
MQ_IMAGE_DEVSERVER_MANIFEST_IFIX=$(MQ_DELIVERY_REGISTRY_FULL_PATH)/$(MQ_IMAGE_DEVSERVER):$(MQ_MANIFEST_TAG)
|
||||||
|
MQ_IMAGE_ADVANCESERVER_MANIFEST_IFIX=$(MQ_DELIVERY_REGISTRY_FULL_PATH)/$(MQ_IMAGE_ADVANCEDSERVER):$(MQ_MANIFEST_TAG)
|
||||||
|
|
||||||
|
PROJECT_DIR := $(shell pwd)
|
||||||
|
BUILD_MANIFEST_FILE := $(PROJECT_DIR)/latest-build-info/build-manifest.yaml
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Build targets
|
||||||
|
###############################################################################
|
||||||
|
.PHONY: default
|
||||||
|
default: build-devserver
|
||||||
|
|
||||||
|
# Build all components (except incubating ones)
|
||||||
|
.PHONY: all
|
||||||
|
all: build-devserver build-advancedserver
|
||||||
|
|
||||||
|
.PHONY: test-all
|
||||||
|
test-all: build-devjmstest test-devserver test-advancedserver
|
||||||
|
|
||||||
|
.PHONY: devserver
|
||||||
|
devserver: build-devserver build-devjmstest test-devserver
|
||||||
|
|
||||||
|
.PHONY: advancedserver
|
||||||
|
advancedserver: build-advancedserver test-advancedserver
|
||||||
|
|
||||||
|
# Build incubating components
|
||||||
|
.PHONY: incubating
|
||||||
|
incubating: build-explorer
|
||||||
|
|
||||||
|
downloads/$(MQ_ARCHIVE_DEV):
|
||||||
|
$(info $(SPACER)$(shell printf $(TITLE)"Downloading IBM MQ Advanced for Developers "$(MQ_VERSION)$(END)))
|
||||||
|
mkdir -p downloads
|
||||||
|
ifneq "$(BUILD_RSYNC_SERVER)" "$(EMPTY)"
|
||||||
|
# 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
|
||||||
|
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
|
||||||
|
./download-basemq.sh -r $(MQ_ARCHIVE_DEV) -v $(MQ_VERSION_VRM)
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
downloads/$(MQ_ARCHIVE):
|
||||||
|
$(info $(SPACER)$(shell printf $(TITLE)"Downloading IBM MQ Advanced "$(MQ_VERSION)$(END)))
|
||||||
|
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)"
|
||||||
|
curl --fail --user $(MQ_ARCHIVE_REPOSITORY_USER):$(MQ_ARCHIVE_REPOSITORY_CREDENTIAL) --request GET "$(MQ_ARCHIVE_REPOSITORY)" --output downloads/$(MQ_ARCHIVE)
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
.PHONY: downloads
|
||||||
|
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 Container tests
|
||||||
|
test/container/vendor:
|
||||||
|
cd test/container && go mod vendor
|
||||||
|
|
||||||
|
# Shortcut to just run the unit tests
|
||||||
|
.PHONY: test-unit
|
||||||
|
test-unit:
|
||||||
|
$(COMMAND) build --target builder --file Dockerfile-server .
|
||||||
|
|
||||||
|
define inspect-image
|
||||||
|
@$(COMMAND) inspect --format ">>> IMAGE UNDER TEST\n RepoTags: {{.RepoTags}}\n RepoDigests: {{.RepoDigests}}\n Created: {{.Created}}\n Architecture: {{.Architecture}}" $1:$2
|
||||||
|
endef
|
||||||
|
|
||||||
|
.PHONY: test-advancedserver
|
||||||
|
test-advancedserver: test/container/vendor
|
||||||
|
$(info $(SPACER)$(shell printf $(TITLE)"Test $(MQ_IMAGE_ADVANCEDSERVER):$(MQ_TAG) on $(shell $(COMMAND) --version)"$(END)))
|
||||||
|
$(call inspect-image,$(MQ_IMAGE_ADVANCEDSERVER),$(MQ_TAG))
|
||||||
|
cd test/container && TEST_IMAGE=$(MQ_IMAGE_ADVANCEDSERVER):$(MQ_TAG) EXPECTED_LICENSE=Production DOCKER_API_VERSION=$(DOCKER_API_VERSION) COMMAND=$(COMMAND) go test -parallel $(NUM_CPU) -timeout $(TEST_TIMEOUT_CONTAINER) $(TEST_OPTS_CONTAINER)
|
||||||
|
|
||||||
|
.PHONY: build-devjmstest
|
||||||
|
build-devjmstest:
|
||||||
|
$(info $(SPACER)$(shell printf $(TITLE)"Build JMS tests for developer config"$(END)))
|
||||||
|
cd test/messaging && $(COMMAND) build --tag $(DEV_JMS_IMAGE) .
|
||||||
|
|
||||||
|
.PHONY: test-devserver
|
||||||
|
test-devserver: test/container/vendor
|
||||||
|
$(info $(SPACER)$(shell printf $(TITLE)"Test $(MQ_IMAGE_DEVSERVER):$(MQ_TAG) on $(shell $(COMMAND) --version)"$(END)))
|
||||||
|
$(call inspect-image,$(MQ_IMAGE_DEVSERVER),$(MQ_TAG))
|
||||||
|
cd test/container && TEST_IMAGE=$(MQ_IMAGE_DEVSERVER):$(MQ_TAG) EXPECTED_LICENSE=Developer DEV_JMS_IMAGE=$(DEV_JMS_IMAGE) IBMJRE=false DOCKER_API_VERSION=$(DOCKER_API_VERSION) COMMAND=$(COMMAND) go test -parallel $(NUM_CPU) -timeout $(TEST_TIMEOUT_CONTAINER) -tags mqdev $(TEST_OPTS_CONTAINER)
|
||||||
|
|
||||||
|
.PHONY: coverage
|
||||||
|
coverage:
|
||||||
|
mkdir coverage
|
||||||
|
|
||||||
|
.PHONY: test-advancedserver-cover
|
||||||
|
test-advancedserver-cover: test/container/vendor coverage
|
||||||
|
$(info $(SPACER)$(shell printf $(TITLE)"Test $(MQ_IMAGE_ADVANCEDSERVER):$(MQ_TAG) with code coverage on $(shell $(COMMAND) --version)"$(END)))
|
||||||
|
rm -f ./coverage/unit*.cov
|
||||||
|
# Run unit tests with coverage, for each package under 'internal'
|
||||||
|
go list -f '{{.Name}}' ./internal/... | xargs -I {} go test -cover -covermode count -coverprofile ./coverage/unit-{}.cov ./internal/{}
|
||||||
|
# ls -1 ./cmd | xargs -I {} go test -cover -covermode count -coverprofile ./coverage/unit-{}.cov ./cmd/{}/...
|
||||||
|
echo 'mode: count' > ./coverage/unit.cov
|
||||||
|
tail -q -n +2 ./coverage/unit-*.cov >> ./coverage/unit.cov
|
||||||
|
go tool cover -html=./coverage/unit.cov -o ./coverage/unit.html
|
||||||
|
|
||||||
|
rm -f ./test/container/coverage/*.cov
|
||||||
|
rm -f ./coverage/container.*
|
||||||
|
mkdir -p ./test/container/coverage/
|
||||||
|
cd test/container && TEST_IMAGE=$(MQ_IMAGE_ADVANCEDSERVER):$(MQ_TAG)-cover TEST_COVER=true DOCKER_API_VERSION=$(DOCKER_API_VERSION) go test $(TEST_OPTS_CONTAINER)
|
||||||
|
echo 'mode: count' > ./coverage/container.cov
|
||||||
|
tail -q -n +2 ./test/container/coverage/*.cov >> ./coverage/container.cov
|
||||||
|
go tool cover -html=./coverage/container.cov -o ./coverage/container.html
|
||||||
|
|
||||||
|
echo 'mode: count' > ./coverage/combined.cov
|
||||||
|
tail -q -n +2 ./coverage/unit.cov ./coverage/container.cov >> ./coverage/combined.cov
|
||||||
|
go tool cover -html=./coverage/combined.cov -o ./coverage/combined.html
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Build functions
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
# Command to build the image
|
||||||
|
# Args: imageName, imageTag, dockerfile, extraArgs, dockerfileTarget
|
||||||
|
# If the ARCH variable has been changed from the default value (arch_go variable), then the `--platform` parameter is added
|
||||||
|
# Args: imageName, imageTag, dockerfile, mqArchive, dockerfileTarget
|
||||||
|
define build-mq
|
||||||
|
rm -f .dockerignore && echo ".git\ndownloads\n!downloads/$4" > .dockerignore
|
||||||
|
$(COMMAND) build \
|
||||||
|
--tag $1:$2 \
|
||||||
|
--file $3 \
|
||||||
|
--build-arg IMAGE_REVISION="$(IMAGE_REVISION)" \
|
||||||
|
--build-arg IMAGE_SOURCE="$(IMAGE_SOURCE)" \
|
||||||
|
--build-arg IMAGE_TAG="$1:$2" \
|
||||||
|
--build-arg MQ_ARCHIVE="downloads/$4" \
|
||||||
|
--label version=$(MQ_VERSION) \
|
||||||
|
--label name=$1 \
|
||||||
|
--label build-date=$(shell date +%Y-%m-%dT%H:%M:%S%z) \
|
||||||
|
--label architecture="$(ARCH)" \
|
||||||
|
--label run="podman run -d -e LICENSE=accept $1:$2" \
|
||||||
|
--label vcs-ref=$(IMAGE_REVISION) \
|
||||||
|
--label vcs-type=git \
|
||||||
|
--label vcs-url=$(IMAGE_SOURCE) \
|
||||||
|
$(if $(findstring $(arch_go),$(ARCH)),,--platform=linux/$(ARCH)) \
|
||||||
|
$(EXTRA_LABELS) \
|
||||||
|
--target $5 \
|
||||||
|
.
|
||||||
|
endef
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Build targets
|
||||||
|
###############################################################################
|
||||||
|
.PHONY: build-advancedserver-host
|
||||||
|
build-advancedserver-host: build-advancedserver
|
||||||
|
|
||||||
|
.PHONY: build-advancedserver
|
||||||
|
build-advancedserver: log-build-env downloads/$(MQ_ARCHIVE) command-version
|
||||||
|
$(info $(SPACER)$(shell printf $(TITLE)"Build $(MQ_IMAGE_ADVANCEDSERVER):$(MQ_TAG)"$(END)))
|
||||||
|
$(call build-mq,$(MQ_IMAGE_ADVANCEDSERVER),$(MQ_TAG),Dockerfile-server,$(MQ_ARCHIVE),mq-server)
|
||||||
|
|
||||||
|
.PHONY: build-devserver-host
|
||||||
|
build-devserver-host: build-devserver
|
||||||
|
|
||||||
|
.PHONY: build-devserver
|
||||||
|
build-devserver: log-build-env downloads/$(MQ_ARCHIVE_DEV) command-version
|
||||||
|
$(info $(shell printf $(TITLE)"Build $(MQ_IMAGE_DEVSERVER):$(MQ_TAG)"$(END)))
|
||||||
|
$(call build-mq,$(MQ_IMAGE_DEVSERVER),$(MQ_TAG),Dockerfile-server,$(MQ_ARCHIVE_DEV),mq-dev-server)
|
||||||
|
|
||||||
|
.PHONY: build-advancedserver-cover
|
||||||
|
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 .
|
||||||
|
|
||||||
|
.PHONY: build-explorer
|
||||||
|
build-explorer: downloads/$(MQ_ARCHIVE_DEV)
|
||||||
|
$(call build-mq,mq-explorer,latest-$(ARCH),incubating/mq-explorer/Dockerfile,$(MQ_ARCHIVE_DEV),mq-explorer)
|
||||||
|
|
||||||
|
.PHONY: build-sdk
|
||||||
|
build-sdk: downloads/$(MQ_ARCHIVE_DEV)
|
||||||
|
$(info $(shell printf $(TITLE)"Build $(MQ_IMAGE_SDK)"$(END)))
|
||||||
|
$(call build-mq,mq-sdk,$(MQ_TAG),incubating/mq-sdk/Dockerfile,$(MQ_SDK_ARCHIVE),mq-sdk)
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Logging targets
|
||||||
|
###############################################################################
|
||||||
|
.PHONY: log-build-env
|
||||||
|
log-build-vars:
|
||||||
|
$(info $(SPACER)$(shell printf $(TITLE)"Build environment"$(END)))
|
||||||
|
@echo arch_uname=$(arch_uname)
|
||||||
|
@echo arch_go=$(arch_go)
|
||||||
|
@echo "ARCH=$(ARCH) (origin:$(origin ARCH))"
|
||||||
|
@echo MQ_VERSION="$(MQ_VERSION) (origin:$(origin MQ_VERSION))"
|
||||||
|
@echo MQ_ARCHIVE="$(MQ_ARCHIVE) (origin:$(origin MQ_ARCHIVE))"
|
||||||
|
@echo MQ_ARCHIVE_DEV_ARCH=$(MQ_ARCHIVE_DEV_ARCH)
|
||||||
|
@echo MQ_ARCHIVE_DEV=$(MQ_ARCHIVE_DEV)
|
||||||
|
@echo MQ_IMAGE_DEVSERVER=$(MQ_IMAGE_DEVSERVER)
|
||||||
|
@echo MQ_IMAGE_ADVANCEDSERVER=$(MQ_IMAGE_ADVANCEDSERVER)
|
||||||
|
@echo COMMAND=$(COMMAND)
|
||||||
|
|
||||||
|
.PHONY: log-build-env
|
||||||
|
log-build-env: log-build-vars
|
||||||
|
$(info $(SPACER)$(shell printf $(TITLE)"Build environment - $(COMMAND) info"$(END)))
|
||||||
|
@echo Command version: $(shell $(COMMAND) --version)
|
||||||
|
$(COMMAND) info
|
||||||
|
|
||||||
|
include Makefile.formatting.mk
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Push/pull targets
|
||||||
|
###############################################################################
|
||||||
|
.PHONY: pull-mq-archive
|
||||||
|
pull-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
|
||||||
|
pull-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
|
||||||
|
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)))
|
||||||
|
$(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) push $(MQ_DELIVERY_REGISTRY_FULL_PATH)/$(MQ_IMAGE_FULL_RELEASE_NAME)
|
||||||
|
|
||||||
|
.PHONY: 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)))
|
||||||
|
$(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) push $(MQ_DELIVERY_REGISTRY_FULL_PATH)/$(MQ_IMAGE_DEV_FULL_RELEASE_NAME)
|
||||||
|
|
||||||
|
.PHONY: pull-advancedserver
|
||||||
|
pull-advancedserver:
|
||||||
|
$(info $(SPACER)$(shell printf $(TITLE)"Pull production image from $(MQ_DELIVERY_REGISTRY_FULL_PATH)"$(END)))
|
||||||
|
$(COMMAND) login $(MQ_DELIVERY_REGISTRY_HOSTNAME) -u $(MQ_DELIVERY_REGISTRY_USER) -p $(MQ_DELIVERY_REGISTRY_CREDENTIAL)
|
||||||
|
$(COMMAND) pull $(MQ_DELIVERY_REGISTRY_FULL_PATH)/$(MQ_IMAGE_FULL_RELEASE_NAME)
|
||||||
|
$(COMMAND) tag $(MQ_DELIVERY_REGISTRY_FULL_PATH)/$(MQ_IMAGE_FULL_RELEASE_NAME) $(MQ_IMAGE_ADVANCEDSERVER)\:$(MQ_TAG)
|
||||||
|
|
||||||
|
.PHONY: pull-devserver
|
||||||
|
pull-devserver:
|
||||||
|
$(info $(SPACER)$(shell printf $(TITLE)"Pull developer image from $(MQ_DELIVERY_REGISTRY_FULL_PATH)"$(END)))
|
||||||
|
$(COMMAND) login $(MQ_DELIVERY_REGISTRY_HOSTNAME) -u $(MQ_DELIVERY_REGISTRY_USER) -p $(MQ_DELIVERY_REGISTRY_CREDENTIAL)
|
||||||
|
$(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)
|
||||||
|
|
||||||
|
.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-manifest
|
||||||
|
build-manifest: build-skopeo-container
|
||||||
|
$(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)))
|
||||||
|
|
||||||
|
$(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)))
|
||||||
|
|
||||||
|
$(eval MQ_IMAGE_DEVSERVER_MANIFEST_DIGEST=$(shell $(COMMAND) run skopeo:latest --override-os linux inspect --creds $(MQ_ARCHIVE_REPOSITORY_USER):$(MQ_ARCHIVE_REPOSITORY_CREDENTIAL) docker://$(MQ_IMAGE_DEVSERVER_MANIFEST_IFIX) | jq -r .Digest))
|
||||||
|
$(eval MQ_IMAGE_ADVANCESERVER_MANIFEST_DIGEST=$(shell $(COMMAND) run skopeo:latest --override-os linux inspect --creds $(MQ_ARCHIVE_REPOSITORY_USER):$(MQ_ARCHIVE_REPOSITORY_CREDENTIAL) docker://$(MQ_IMAGE_ADVANCESERVER_MANIFEST_IFIX) | jq -r .Digest))
|
||||||
|
$(info $(shell printf "** Determined the built has a advanceserver digest for ifix of $(MQ_IMAGE_ADVANCESERVER_MANIFEST_DIGEST)**"$(END)))
|
||||||
|
$(info $(shell printf "** Determined the built has a devserver digest for ifix of $(MQ_IMAGE_DEVSERVER_MANIFEST_DIGEST)**"$(END)))
|
||||||
|
@./travis-build-scripts/create-build-manifest.sh -f $(BUILD_MANIFEST_FILE) -o $(MQ_MANIFEST_TAG) -t $(MQ_IMAGE_DEVSERVER_AMD64_DIGEST) -u ${MQ_IMAGE_DEVSERVER_S390X_DIGEST} -p ${MQ_IMAGE_DEVSERVER_PPC64LE_DIGEST} -r ${MQ_IMAGE_DEVSERVER_MANIFEST_DIGEST} -n $(MQ_IMAGE_ADVANCEDSERVER_AMD64_DIGEST) -a ${MQ_IMAGE_ADVANCEDSERVER_S390X_DIGEST} -m ${MQ_IMAGE_ADVANCEDSERVER_PPC64LE_DIGEST} -s ${MQ_IMAGE_ADVANCESERVER_MANIFEST_DIGEST}
|
||||||
|
|
||||||
|
.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
|
||||||
|
clean:
|
||||||
|
rm -rf ./coverage
|
||||||
|
rm -rf ./build
|
||||||
|
rm -rf ./deps
|
||||||
|
|
||||||
|
.PHONY: install-build-deps
|
||||||
|
install-build-deps:
|
||||||
|
ARCH=$(ARCH) ./install-build-deps.sh
|
||||||
|
|
||||||
|
.PHONY: install-credential-helper
|
||||||
|
install-credential-helper:
|
||||||
|
ifeq ($(ARCH),amd64)
|
||||||
|
ARCH=$(ARCH) ./travis-build-scripts/install-credential-helper.sh
|
||||||
|
endif
|
||||||
|
|
||||||
|
.PHONY: build-cov
|
||||||
|
build-cov:
|
||||||
|
mkdir -p build
|
||||||
|
cd build; go test -c -covermode=count ../cmd/runmqserver
|
||||||
|
|
||||||
|
.PHONY: precommit
|
||||||
|
precommit: fmt lint
|
||||||
|
|
||||||
|
.PHONY: fmt
|
||||||
|
fmt: $(addsuffix /$(wildcard *.go), $(GO_PKG_DIRS))
|
||||||
|
go fmt $(addsuffix /..., $(GO_PKG_DIRS))
|
||||||
|
|
||||||
|
.PHONY: lint
|
||||||
|
lint: $(addsuffix /$(wildcard *.go), $(GO_PKG_DIRS))
|
||||||
|
@# This expression is necessary because /... includes the vendor directory in golint
|
||||||
|
@# As of 11/04/2018 there is an open issue to fix it: https://github.com/golang/lint/issues/320
|
||||||
|
golint -set_exit_status $(sort $(dir $(wildcard $(addsuffix /*/*.go, $(GO_PKG_DIRS)))))
|
||||||
|
|
||||||
|
.PHONY: gosec
|
||||||
|
gosec:
|
||||||
|
$(info $(SPACER)$(shell printf "Running gosec test"$(END)))
|
||||||
|
@gosecrc=0; gosec -fmt=json -out=gosec_results.json cmd/... internal/... 2> /dev/null || gosecrc=$$?; \
|
||||||
|
cat gosec_results.json | jq '{"GolangErrors": (.["Golang errors"]|length>0),"Issues":(.Issues|length>0)}' | grep 'true' >/dev/null ;\
|
||||||
|
if [ $$? -eq 0 ] || [ $$gosecrc -ne 0 ]; then \
|
||||||
|
printf "FAILURE: Issues found running gosec - see gosec_results.json\n" ;\
|
||||||
|
cat "gosec_results.json" ;\
|
||||||
|
exit 1 ;\
|
||||||
|
else \
|
||||||
|
printf "gosec found no issues\n" ;\
|
||||||
|
cat "gosec_results.json" ;\
|
||||||
|
fi
|
||||||
|
|
||||||
|
.PHONY: update-release-information
|
||||||
|
update-release-information:
|
||||||
|
sed -i.bak 's/ARG MQ_ARCHIVE=.*-LinuxX64.tar.gz"/ARG MQ_ARCHIVE="downloads\/$(MQ_VERSION)-IBM-MQ-Advanced-for-Developers-Non-Install-LinuxX64.tar.gz"/g' Dockerfile-server && rm Dockerfile-server.bak
|
||||||
|
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_VR=$(subst $(SPACE),.,$(wordlist 1,2,$(subst .,$(SPACE),$(MQ_VERSION_VRM)))))
|
||||||
|
sed -i.bak 's/knowledgecenter\/SSFKSJ_.*\/com/knowledgecenter\/SSFKSJ_${MQ_VERSION_VR}.0\/com/g' docs/usage.md && rm docs/usage.md.bak
|
||||||
|
$(eval MQ_VERSION_VRM_FLAT=$(shell echo '${MQ_VERSION_VRM}' | sed "s/\./_/g"))
|
||||||
|
sed -i.bak 's/MQ_._._._ARCHIVE_REPOSITORY/MQ_${MQ_VERSION_VRM_FLAT}_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 "20" || ("$(word 1,$(subst ., ,$(COMMAND_CLIENT_VERSION)))" -eq "20" && "$(word 2,$(subst ., ,$(COMMAND_CLIENT_VERSION)))" -ge "10") || (echo "Error: Docker client 20.10 or greater is required" && exit 1)
|
||||||
|
@test "$(word 1,$(subst ., ,$(COMMAND_SERVER_VERSION)))" -ge "20" || ("$(word 1,$(subst ., ,$(COMMAND_SERVER_VERSION)))" -eq "20" && "$(word 2,$(subst ., ,$(COMMAND_CLIENT_VERSION)))" -ge "10") || (echo "Error: Docker server 20.10 or greater is required" && exit 1)
|
||||||
|
endif
|
||||||
|
ifneq (,$(findstring podman,$(COMMAND)))
|
||||||
|
@test "$(word 1,$(subst ., ,$(PODMAN_VERSION)))" -ge "4" || (echo "Error: Podman version 4.4 or greater is required" && exit 1)
|
||||||
|
endif
|
||||||
|
|
||||||
|
.PHONY: commit-build-manifest
|
||||||
|
commit-build-manifest:
|
||||||
|
@echo "The value of CURRENT_BRANCH is: $(TRAVIS_BRANCH)"
|
||||||
|
@echo "The value of BUILD_MANIFEST_FILE is: $(BUILD_MANIFEST_FILE)"
|
||||||
|
echo "Checking git status..."
|
||||||
|
git status
|
||||||
|
echo "Staging changes..."
|
||||||
|
git add $(BUILD_MANIFEST_FILE)
|
||||||
|
echo "Committing changes..."
|
||||||
|
git commit -m "[ci skip]: Commit the digests for ifix and the build-manifest back to the branch"
|
||||||
|
echo "Pulling latest changes from remote..."
|
||||||
|
git pull --rebase origin $(TRAVIS_BRANCH)
|
||||||
|
echo "Pushing changes to remote..."
|
||||||
|
git push origin HEAD:$(TRAVIS_BRANCH)
|
||||||
17
Makefile.formatting.mk
Normal file
17
Makefile.formatting.mk
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
GREEN="\033[32m"
|
||||||
|
RED="\033[31m"
|
||||||
|
BLUE="\033[34m"
|
||||||
|
PURPLE="\033[35m"
|
||||||
|
AQUA="\033[36m"
|
||||||
|
|
||||||
|
END="\033[0m"
|
||||||
|
|
||||||
|
UNDERLINE="\033[4m"
|
||||||
|
BOLD="\033[1m"
|
||||||
|
|
||||||
|
TITLE=$(BLUE)$(BOLD)$(UNDERLINE)
|
||||||
|
|
||||||
|
define SPACER
|
||||||
|
|
||||||
|
|
||||||
|
endef
|
||||||
59
README.md
Normal file
59
README.md
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
# IBM MQ container
|
||||||
|
|
||||||
|
|
||||||
|
[](https://travis-ci.org/ibm-messaging/mq-container)
|
||||||
|
|
||||||
|
**Note**: The `master` branch may be in an *unstable or even broken state* during development.
|
||||||
|
To get a stable version, please use the correct [branch](https://github.com/ibm-messaging/mq-container/branches) for your MQ version, instead of the `master` branch.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Run [IBM® MQ](http://www-03.ibm.com/software/products/en/ibm-mq) in a container.
|
||||||
|
|
||||||
|
You can build an image containing either IBM MQ Advanced, or IBM MQ Advanced for Developers. The developer image includes a [default developer configuration](docs/developer-config.md), to make it easier to get started. There is also an [incubating](incubating) folder for additional images for other MQ components, which you might find useful.
|
||||||
|
|
||||||
|
## Build
|
||||||
|
|
||||||
|
After extracting the code from this repository, you can follow the [build documentation](docs/building.md) to build an image.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
See the [usage documentation](docs/usage.md) for details on how to run a container.
|
||||||
|
|
||||||
|
Note that in order to use the image, it is necessary to accept the terms of the [IBM MQ license](#license).
|
||||||
|
|
||||||
|
### Environment variables supported by this image
|
||||||
|
|
||||||
|
- **LICENSE** - Set this to `accept` to agree to the MQ Advanced for Developers license. If you wish to see the license you can set this to `view`.
|
||||||
|
- **LANG** - Set this to the language you would like the license to be printed in.
|
||||||
|
- **MQ_QMGR_NAME** - Set this to the name you want your Queue Manager to be created with.
|
||||||
|
- **MQ_QMGR_LOG_FILE_PAGES** - Set this to control the value for LogFilePages passed to the "crtmqm" command. Cannot be changed after queue manager creation.
|
||||||
|
- **MQ_LOGGING_CONSOLE_SOURCE** - Specifies a comma-separated list of sources for logs which are mirrored to the container's stdout. The valid values are "qmgr", "web" and "mqsc". Defaults to "qmgr,web".
|
||||||
|
- **MQ_LOGGING_CONSOLE_FORMAT** - Changes the format of the logs which are printed on the container's stdout. Set to "json" to use JSON format (JSON object per line); set to "basic" to use a simple human-readable format. Defaults to "basic".
|
||||||
|
- **MQ_LOGGING_CONSOLE_EXCLUDE_ID** - Excludes log messages with the specified ID. The log messages still appear in the log file on disk, but are excluded from the container's stdout. Defaults to "AMQ5041I,AMQ5052I,AMQ5051I,AMQ5037I,AMQ5975I".
|
||||||
|
- **MQ_ENABLE_METRICS** - Set this to `true` to generate Prometheus metrics for your Queue Manager.
|
||||||
|
|
||||||
|
See the [default developer configuration docs](docs/developer-config.md) for the extra environment variables supported by the MQ Advanced for Developers image.
|
||||||
|
|
||||||
|
### Kubernetes
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
For issues relating specifically to the container image or Helm chart, please use the [GitHub issue tracker](https://github.com/ibm-messaging/mq-container/issues). Pull requests are not currently accepted.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
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:
|
||||||
|
|
||||||
|
- [IBM MQ Advanced for Developers](http://www14.software.ibm.com/cgi-bin/weblap/lap.pl?la_formnum=Z125-3301-14&li_formnum=L-CLXQ-ADXTK3) (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-EHXT-MQCRN9) (International Program License Agreement). This license may be viewed from an image using the `LICENSE=view` environment variable as described above or by following the link above.
|
||||||
|
|
||||||
|
Note: The IBM MQ Advanced for Developers license does not permit further distribution and the terms restrict usage to a developer machine.
|
||||||
|
|
||||||
|
|
||||||
|
## Copyright
|
||||||
|
|
||||||
|
© Copyright IBM Corporation 2015, 2023
|
||||||
2
authservice/mqsimpleauth/.gitignore
vendored
Normal file
2
authservice/mqsimpleauth/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
/testSecret*
|
||||||
|
/*.log
|
||||||
59
authservice/mqsimpleauth/Makefile
Normal file
59
authservice/mqsimpleauth/Makefile
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
# © Copyright IBM Corporation 2017, 2022
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
# This Makefile expects the following to be installed:
|
||||||
|
# - gcc
|
||||||
|
# - ldd
|
||||||
|
# - MQ SDK (mqm_r library, plus header files)
|
||||||
|
|
||||||
|
SRC_DIR = src
|
||||||
|
BUILD_DIR = ./build
|
||||||
|
ARCH ?= $(if $(findstring x86_64,$(shell uname -m)),amd64,$(if $(findstring aarch64,$(shell uname -m)),aarch64,$(shell uname -m)))
|
||||||
|
|
||||||
|
# Flags passed to the C compiler. Need to use gnu11 to get POSIX functions needed for file locking.
|
||||||
|
CFLAGS.amd64 := -m64
|
||||||
|
CFLAGS.ppc64le := -m64
|
||||||
|
CFLAGS.s390x := -m64
|
||||||
|
# -m64 is not a valid compiler option on aarch64/arm64 (ARM)
|
||||||
|
CFLAGS.arm64 :=
|
||||||
|
CFLAGS += -std=gnu11 -fPIC -Wall ${CFLAGS.${ARCH}}
|
||||||
|
|
||||||
|
LIB_MQ = -L/opt/mqm/lib64 -lmqm_r
|
||||||
|
|
||||||
|
all: $(BUILD_DIR)/mqsimpleauth.so $(BUILD_DIR)/simpleauth_test $(BUILD_DIR)/log_test
|
||||||
|
|
||||||
|
$(BUILD_DIR)/log.o : $(SRC_DIR)/log.c $(SRC_DIR)/log.h
|
||||||
|
mkdir -p ${dir $@}
|
||||||
|
gcc $(CFLAGS) -c $(SRC_DIR)/log.c -o $@
|
||||||
|
|
||||||
|
$(BUILD_DIR)/log_test : $(BUILD_DIR)/log.o
|
||||||
|
mkdir -p ${dir $@}
|
||||||
|
gcc $(CFLAGS) $(SRC_DIR)/log_test.c $^ -o $@
|
||||||
|
# Run Logging tests, and print log if they fail
|
||||||
|
$@ || (cat log_test*.log && exit 1)
|
||||||
|
|
||||||
|
$(BUILD_DIR)/simpleauth.o : $(SRC_DIR)/simpleauth.c $(SRC_DIR)/simpleauth.h
|
||||||
|
mkdir -p ${dir $@}
|
||||||
|
gcc $(CFLAGS) -c $(SRC_DIR)/simpleauth.c -o $@
|
||||||
|
|
||||||
|
$(BUILD_DIR)/simpleauth_test : $(BUILD_DIR)/simpleauth.o $(BUILD_DIR)/log.o
|
||||||
|
mkdir -p ${dir $@}
|
||||||
|
gcc $(CFLAGS) -lpthread $(SRC_DIR)/simpleauth_test.c $^ -o $@
|
||||||
|
# Run SimpleAuth tests, and print log if they fail
|
||||||
|
$@ || (cat simpleauth_test*.log && exit 1)
|
||||||
|
|
||||||
|
$(BUILD_DIR)/mqsimpleauth.so : $(BUILD_DIR)/log.o $(BUILD_DIR)/simpleauth.o
|
||||||
|
mkdir -p ${dir $@}
|
||||||
|
gcc $(CFLAGS) -I/opt/mqm/inc -D_REENTRANT $(LIB_MQ) -Wl,-rpath,/opt/mqm/lib64 -Wl,-rpath,/usr/lib64 -shared $(SRC_DIR)/mqsimpleauth.c $^ -o $@
|
||||||
|
ldd $@
|
||||||
162
authservice/mqsimpleauth/src/log.c
Normal file
162
authservice/mqsimpleauth/src/log.c
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
/*
|
||||||
|
© Copyright IBM Corporation 2021, 2022
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
FILE *fp = NULL;
|
||||||
|
int pid;
|
||||||
|
char hostname[255];
|
||||||
|
bool debug = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether debugging is enabled or not, using an environment variable.
|
||||||
|
*/
|
||||||
|
void init_debug(){
|
||||||
|
char *debug_env = getenv("DEBUG");
|
||||||
|
if (debug_env != NULL)
|
||||||
|
{
|
||||||
|
// Enable debug logging if the DEBUG environment variable is set
|
||||||
|
if (strncmp(debug_env, "true", 4) || strncmp(debug_env, "1", 1))
|
||||||
|
{
|
||||||
|
debug = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal function to initialize the log with the given file mode.
|
||||||
|
*/
|
||||||
|
int log_init_internal(char *filename, const char *mode)
|
||||||
|
{
|
||||||
|
int result = 0;
|
||||||
|
pid = getpid();
|
||||||
|
hostname[254] = '\0';
|
||||||
|
gethostname(hostname, 254);
|
||||||
|
if (!fp)
|
||||||
|
{
|
||||||
|
fp = fopen(filename, "a");
|
||||||
|
if (fp)
|
||||||
|
{
|
||||||
|
// Disable buffering for this file
|
||||||
|
setbuf(fp, NULL);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
init_debug();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int log_init_reset(char *filename)
|
||||||
|
{
|
||||||
|
// Open the log file for writing (overwrite if it already exists)
|
||||||
|
return log_init_internal(filename, "w");
|
||||||
|
}
|
||||||
|
|
||||||
|
int log_init(char *filename)
|
||||||
|
{
|
||||||
|
// Open the log file file for appending
|
||||||
|
return log_init_internal(filename, "a");
|
||||||
|
}
|
||||||
|
|
||||||
|
void log_init_file(FILE *f)
|
||||||
|
{
|
||||||
|
fp = f;
|
||||||
|
init_debug();
|
||||||
|
}
|
||||||
|
|
||||||
|
void log_close()
|
||||||
|
{
|
||||||
|
if (fp)
|
||||||
|
{
|
||||||
|
fclose(fp);
|
||||||
|
fp = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void log_printf(const char *source_file, int source_line, const char *level, const char *format, ...)
|
||||||
|
{
|
||||||
|
if (fp)
|
||||||
|
{
|
||||||
|
// If this is a DEBUG message, and debugging is off
|
||||||
|
if ((strncmp(level, "DEBUG", 5) == 0) && !debug)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
char buf[1024] = "";
|
||||||
|
char *cur = buf;
|
||||||
|
char* const end = buf + sizeof buf;
|
||||||
|
char date_buf[70];
|
||||||
|
struct tm *utc;
|
||||||
|
time_t t;
|
||||||
|
struct timeval now;
|
||||||
|
|
||||||
|
gettimeofday(&now, NULL);
|
||||||
|
t = now.tv_sec;
|
||||||
|
t = time(NULL);
|
||||||
|
utc = gmtime(&t);
|
||||||
|
|
||||||
|
cur += snprintf(cur, end-cur, "{");
|
||||||
|
cur += snprintf(cur, end-cur, "\"loglevel\":\"%s\"", level);
|
||||||
|
// Print ISO-8601 time and date
|
||||||
|
if (strftime(date_buf, sizeof date_buf, "%FT%T", utc))
|
||||||
|
{
|
||||||
|
// Round microseconds down to milliseconds, for consistency
|
||||||
|
cur += snprintf(cur, end-cur, ", \"ibm_datetime\":\"%s.%03ldZ\"", date_buf, now.tv_usec / (long)1000);
|
||||||
|
}
|
||||||
|
cur += snprintf(cur, end-cur, ", \"ibm_processId\":\"%d\"", pid);
|
||||||
|
cur += snprintf(cur, end-cur, ", \"host\":\"%s\"", hostname);
|
||||||
|
cur += snprintf(cur, end-cur, ", \"module\":\"%s:%d\"", source_file, source_line);
|
||||||
|
cur += snprintf(cur, end-cur, ", \"message\":\"");
|
||||||
|
|
||||||
|
if (strncmp(level, "DEBUG", 5) == 0)
|
||||||
|
{
|
||||||
|
// Add a prefix on any debug messages
|
||||||
|
cur += snprintf(cur, end-cur, "mqsimpleauth: ");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print log message, using varargs
|
||||||
|
va_list args;
|
||||||
|
va_start(args, format);
|
||||||
|
cur += vsnprintf(cur, end-cur, format, args);
|
||||||
|
va_end(args);
|
||||||
|
cur += snprintf(cur, end-cur, "\"}\n");
|
||||||
|
|
||||||
|
// Important: Just do one file write, to prevent problems with multi-threading.
|
||||||
|
// This only works if the log message is not too long for the buffer.
|
||||||
|
fprintf(fp, "%s", buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int trimmed_len(char *s, int max_len)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = max_len - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
if (s[i] != ' ')
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return i+1;
|
||||||
|
}
|
||||||
70
authservice/mqsimpleauth/src/log.h
Normal file
70
authservice/mqsimpleauth/src/log.h
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
© Copyright IBM Corporation 2021, 2022
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _LOG_H
|
||||||
|
#define _LOG_H
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the log to use the given file name, wiping any existing contents.
|
||||||
|
*/
|
||||||
|
int log_init_reset(char *filename);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the log to use the given file name.
|
||||||
|
*/
|
||||||
|
int log_init(char *filename);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the log with an existing file handle.
|
||||||
|
*/
|
||||||
|
void log_init_file(FILE *f);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write a message to the log file, based on a printf format string.
|
||||||
|
*
|
||||||
|
* @param source_file the name of the source code file submitting this log message
|
||||||
|
* @param source_line the line of code in the source file
|
||||||
|
* @param level the log level, one of "DEBUG", "INFO" or "ERROR"
|
||||||
|
* @param format the printf format string for the message
|
||||||
|
*/
|
||||||
|
void log_printf(const char *source_file, int source_line, const char *level, const char *format, ...);
|
||||||
|
|
||||||
|
void log_close();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Variadic macro to write an informational message to the log file, based on a printf format string.
|
||||||
|
*/
|
||||||
|
#define log_infof(format,...) log_printf(__FILE__, __LINE__, "INFO", format, ##__VA_ARGS__)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Variadic macro to write an error message to the log file, based on a printf format string.
|
||||||
|
*/
|
||||||
|
#define log_errorf(format,...) log_printf(__FILE__, __LINE__, "ERROR", format, ##__VA_ARGS__)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Variadic macro to write a debug message to the log file, based on a printf format string.
|
||||||
|
*/
|
||||||
|
#define log_debugf(format,...) log_printf(__FILE__, __LINE__, "DEBUG", format, ##__VA_ARGS__)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the length of the string when trimmed of trailing spaces.
|
||||||
|
* IBM MQ uses fixed length strings, so this function can be used to print
|
||||||
|
* a trimmed version of a string using the "%.*s" printf format string.
|
||||||
|
* For example, `log_printf("%.*s", trimmed_len(fw_str, 48), fw_str)`
|
||||||
|
*/
|
||||||
|
int trimmed_len(char *s, int);
|
||||||
|
|
||||||
|
#endif
|
||||||
120
authservice/mqsimpleauth/src/log_test.c
Normal file
120
authservice/mqsimpleauth/src/log_test.c
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
/*
|
||||||
|
© Copyright IBM Corporation 2022
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "log.h"
|
||||||
|
|
||||||
|
// Headers for multi-threaded tests
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
// Start a test and log the function name
|
||||||
|
#define test_start() printf("=== RUN: %s\n", __func__)
|
||||||
|
|
||||||
|
// Indicate test has passed
|
||||||
|
#define test_pass() printf("--- PASS: %s\n", __func__)
|
||||||
|
|
||||||
|
// The length of strings used in the tests
|
||||||
|
#define STR_LEN 5
|
||||||
|
|
||||||
|
// Indicate test has failed
|
||||||
|
void test_fail(const char *test_name)
|
||||||
|
{
|
||||||
|
printf("--- FAIL: %s\n", test_name);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print a fixed-width string in hexadecimal
|
||||||
|
void print_hex(char fw_string[STR_LEN])
|
||||||
|
{
|
||||||
|
printf("[");
|
||||||
|
for (int i=0; i<STR_LEN; i++)
|
||||||
|
{
|
||||||
|
printf("%02x", fw_string[i]);
|
||||||
|
if (i < STR_LEN-1)
|
||||||
|
printf(",");
|
||||||
|
}
|
||||||
|
printf("]");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Tests for string manipulation
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void test_trimmed_len(const char *test_name, char fw_string[STR_LEN], int expected_len)
|
||||||
|
{
|
||||||
|
printf("=== RUN: %s\n", test_name);
|
||||||
|
int len;
|
||||||
|
// Create a copy of the fixed-width string
|
||||||
|
char fw_string2[STR_LEN];
|
||||||
|
memcpy(fw_string2, fw_string, STR_LEN * sizeof(char));
|
||||||
|
// Call the function under test
|
||||||
|
len = trimmed_len(fw_string, STR_LEN);
|
||||||
|
// Check the result is correct
|
||||||
|
if (len != expected_len)
|
||||||
|
{
|
||||||
|
printf("%s: Expected result to be %d; got %d\n", __func__, expected_len, len);
|
||||||
|
test_fail(test_name);
|
||||||
|
}
|
||||||
|
// Check that the original string has not been changed
|
||||||
|
for (int i=0; i<STR_LEN; i++)
|
||||||
|
{
|
||||||
|
if (fw_string[i] != fw_string2[i])
|
||||||
|
{
|
||||||
|
printf("%c-%c\n", fw_string[i], fw_string2[i]);
|
||||||
|
printf("%s: Expected string to be identical to input hex ", __func__);
|
||||||
|
print_hex(fw_string2);
|
||||||
|
printf("; got hex ");
|
||||||
|
print_hex(fw_string);
|
||||||
|
printf("\n");
|
||||||
|
test_fail(test_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("--- PASS: %s\n", test_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_trimmed_len_normal()
|
||||||
|
{
|
||||||
|
char fw_string[STR_LEN] = {'a','b','c',' ',' '};
|
||||||
|
test_trimmed_len(__func__, fw_string, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_trimmed_len_full()
|
||||||
|
{
|
||||||
|
char fw_string[STR_LEN] = {'a','b','c','d','e'};
|
||||||
|
test_trimmed_len(__func__, fw_string, 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_trimmed_len_empty()
|
||||||
|
{
|
||||||
|
char fw_string[STR_LEN] = {' ',' ',' ',' ',' '};
|
||||||
|
test_trimmed_len(__func__, fw_string, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
// Turn on debugging for the tests
|
||||||
|
setenv("DEBUG", "true", true);
|
||||||
|
log_init("log_test.log");
|
||||||
|
test_trimmed_len_normal();
|
||||||
|
test_trimmed_len_full();
|
||||||
|
test_trimmed_len_empty();
|
||||||
|
log_close();
|
||||||
|
}
|
||||||
1
authservice/mqsimpleauth/src/mqAdminPassword
Normal file
1
authservice/mqsimpleauth/src/mqAdminPassword
Normal file
@@ -0,0 +1 @@
|
|||||||
|
fred:$2y$05$3Fp9
|
||||||
336
authservice/mqsimpleauth/src/mqsimpleauth.c
Normal file
336
authservice/mqsimpleauth/src/mqsimpleauth.c
Normal file
@@ -0,0 +1,336 @@
|
|||||||
|
/*
|
||||||
|
© Copyright IBM Corporation 2021, 2024
|
||||||
|
|
||||||
|
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 "simpleauth.h"
|
||||||
|
|
||||||
|
// Declare the internal functions that implement the interface
|
||||||
|
MQZ_INIT_AUTHORITY MQStart;
|
||||||
|
static MQZ_AUTHENTICATE_USER mqsimpleauth_authenticate_user;
|
||||||
|
static MQZ_FREE_USER mqsimpleauth_free_user;
|
||||||
|
static MQZ_TERM_AUTHORITY mqsimpleauth_terminate;
|
||||||
|
|
||||||
|
#define LOG_FILE "/var/mqm/errors/simpleauth.json"
|
||||||
|
#define NAME "MQ Advanced for Developers custom authentication service"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialization and entrypoint for the dynamically loaded
|
||||||
|
* authorization installable service. It registers the addresses of the
|
||||||
|
* other functions which are to be called by the queue manager.
|
||||||
|
*
|
||||||
|
* This function is called whenever the module is loaded. The Options
|
||||||
|
* field will show whether it's a PRIMARY (i.e. during qmgr startup) or
|
||||||
|
* SECONDARY.
|
||||||
|
*/
|
||||||
|
void MQENTRY MQStart(
|
||||||
|
MQHCONFIG hc,
|
||||||
|
MQLONG Options,
|
||||||
|
MQCHAR48 QMgrName,
|
||||||
|
MQLONG ComponentDataLength,
|
||||||
|
PMQBYTE ComponentData,
|
||||||
|
PMQLONG Version,
|
||||||
|
PMQLONG pCompCode,
|
||||||
|
PMQLONG pReason)
|
||||||
|
{
|
||||||
|
MQLONG CC = MQCC_OK;
|
||||||
|
MQLONG Reason = MQRC_NONE;
|
||||||
|
int log_rc = 0;
|
||||||
|
|
||||||
|
if (Options == MQZIO_PRIMARY)
|
||||||
|
{
|
||||||
|
// Reset the log file. The file could still get large if debug is turned on,
|
||||||
|
// but this is a simpler solution for now.
|
||||||
|
log_rc = log_init_reset(LOG_FILE);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
log_rc = log_init(LOG_FILE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (log_rc != 0)
|
||||||
|
{
|
||||||
|
CC = MQCC_FAILED;
|
||||||
|
Reason = MQRC_INITIALIZATION_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Options == MQZIO_PRIMARY)
|
||||||
|
{
|
||||||
|
log_infof("Initializing %s", NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
log_debugf("MQStart options=%s qmgr=%.*s", ((Options == MQZIO_SECONDARY) ? "Secondary" : "Primary"), trimmed_len(QMgrName, MQ_Q_MGR_NAME_LENGTH), QMgrName);
|
||||||
|
|
||||||
|
// 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)mqsimpleauth_terminate, &CC, &Reason);
|
||||||
|
}
|
||||||
|
if (CC == MQCC_OK)
|
||||||
|
{
|
||||||
|
hc->MQZEP_Call(hc, MQZID_AUTHENTICATE_USER, (PMQFUNC)mqsimpleauth_authenticate_user, &CC, &Reason);
|
||||||
|
}
|
||||||
|
if (CC == MQCC_OK)
|
||||||
|
{
|
||||||
|
hc->MQZEP_Call(hc, MQZID_FREE_USER, (PMQFUNC)mqsimpleauth_free_user, &CC, &Reason);
|
||||||
|
}
|
||||||
|
*Version = MQZAS_VERSION_6;
|
||||||
|
*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 mqsimpleauth_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 = simpleauth_authenticate_user(csp_user, csp_pass);
|
||||||
|
|
||||||
|
if (auth_result == SIMPLEAUTH_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 simpleauth file does not have an entry for this user
|
||||||
|
else if (auth_result == SIMPLEAUTH_INVALID_USER)
|
||||||
|
{
|
||||||
|
*pCompCode = MQCC_WARNING;
|
||||||
|
*pReason = MQRC_NONE;
|
||||||
|
// Tell the queue manager to continue trying other authorization services, as they might have the user.
|
||||||
|
*pContinuation = MQZCI_CONTINUE;
|
||||||
|
log_debugf(
|
||||||
|
"User authentication failed due to invalid user. user=%.*s effuser=%.*s applname=%.*s csp_user=%s cc=%d reason=%d",
|
||||||
|
trimmed_len(pIdentityContext->UserIdentifier, MQ_USER_ID_LENGTH),
|
||||||
|
pIdentityContext->UserIdentifier,
|
||||||
|
trimmed_len(pApplicationContext->EffectiveUserID, MQ_USER_ID_LENGTH),
|
||||||
|
pApplicationContext->EffectiveUserID,
|
||||||
|
trimmed_len(pApplicationContext->ApplName, MQ_APPL_NAME_LENGTH),
|
||||||
|
pApplicationContext->ApplName,
|
||||||
|
csp_user,
|
||||||
|
*pCompCode,
|
||||||
|
*pReason);
|
||||||
|
}
|
||||||
|
// If the simpleauth file has an entry for this user, but the password supplied is incorrect
|
||||||
|
else if (auth_result == SIMPLEAUTH_INVALID_PASSWORD)
|
||||||
|
{
|
||||||
|
*pCompCode = MQCC_WARNING;
|
||||||
|
*pReason = MQRC_NOT_AUTHORIZED;
|
||||||
|
// Tell the queue manager to stop trying other authorization services.
|
||||||
|
*pContinuation = MQZCI_STOP;
|
||||||
|
log_debugf(
|
||||||
|
"User authentication failed due to invalid password. user=%.*s effuser=%.*s applname=%.*s csp_user=%s cc=%d reason=%d",
|
||||||
|
trimmed_len(pIdentityContext->UserIdentifier, MQ_USER_ID_LENGTH),
|
||||||
|
pIdentityContext->UserIdentifier,
|
||||||
|
trimmed_len(pApplicationContext->EffectiveUserID, MQ_USER_ID_LENGTH),
|
||||||
|
pApplicationContext->EffectiveUserID,
|
||||||
|
trimmed_len(pApplicationContext->ApplName, MQ_APPL_NAME_LENGTH),
|
||||||
|
pApplicationContext->ApplName,
|
||||||
|
csp_user,
|
||||||
|
*pCompCode,
|
||||||
|
*pReason);
|
||||||
|
}
|
||||||
|
if (csp_user)
|
||||||
|
{
|
||||||
|
free(csp_user);
|
||||||
|
}
|
||||||
|
if (csp_pass)
|
||||||
|
{
|
||||||
|
free(csp_pass);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called during the connection of any application.
|
||||||
|
* For more information on the parameters, see https://www.ibm.com/support/knowledgecenter/en/SSFKSJ_latest/com.ibm.mq.ref.dev.doc/q110090_.html
|
||||||
|
*/
|
||||||
|
static void MQENTRY mqsimpleauth_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)
|
||||||
|
{
|
||||||
|
mqsimpleauth_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 = simpleauth_valid_user(spuser);
|
||||||
|
if (valid_user)
|
||||||
|
{
|
||||||
|
// An OK completion code means MQ will accept this user is authenticated
|
||||||
|
*pCompCode = MQCC_OK;
|
||||||
|
*pReason = MQRC_NONE;
|
||||||
|
*pContinuation = MQZCI_STOP;
|
||||||
|
memcpy(pIdentityContext->UserIdentifier, spuser, sizeof(pIdentityContext->UserIdentifier));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
log_debugf(
|
||||||
|
"User authentication failed user=%.*s effuser=%.*s applname=%.*s cspuser=%s cc=%d reason=%d",
|
||||||
|
trimmed_len(pIdentityContext->UserIdentifier, MQ_USER_ID_LENGTH),
|
||||||
|
pIdentityContext->UserIdentifier,
|
||||||
|
trimmed_len(pApplicationContext->EffectiveUserID, MQ_USER_ID_LENGTH),
|
||||||
|
pApplicationContext->EffectiveUserID,
|
||||||
|
trimmed_len(pApplicationContext->ApplName, MQ_APPL_NAME_LENGTH),
|
||||||
|
pApplicationContext->ApplName,
|
||||||
|
spuser,
|
||||||
|
*pCompCode,
|
||||||
|
*pReason);
|
||||||
|
}
|
||||||
|
if (spuser)
|
||||||
|
{
|
||||||
|
free(spuser);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called during MQDISC, as the inverse of the call to authenticate.
|
||||||
|
*/
|
||||||
|
static void MQENTRY mqsimpleauth_free_user(
|
||||||
|
PMQCHAR pQMgrName,
|
||||||
|
PMQZFP pFreeParms,
|
||||||
|
PMQBYTE pComponentData,
|
||||||
|
PMQLONG pContinuation,
|
||||||
|
|
||||||
|
PMQLONG pCompCode,
|
||||||
|
PMQLONG pReason)
|
||||||
|
{
|
||||||
|
log_debugf("mqsimpleauth_freeuser()");
|
||||||
|
*pCompCode = MQCC_WARNING;
|
||||||
|
*pReason = MQRC_NONE;
|
||||||
|
*pContinuation = MQZCI_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the authorization service is terminated.
|
||||||
|
*/
|
||||||
|
static void MQENTRY mqsimpleauth_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;
|
||||||
|
}
|
||||||
|
|
||||||
153
authservice/mqsimpleauth/src/simpleauth.c
Normal file
153
authservice/mqsimpleauth/src/simpleauth.c
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
/*
|
||||||
|
© Copyright IBM Corporation 2021, 2024
|
||||||
|
|
||||||
|
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 "simpleauth.h"
|
||||||
|
#include <linux/limits.h>
|
||||||
|
|
||||||
|
const char *_mq_app_secret_file = MQ_APP_SECRET_FILE_DEFAULT;
|
||||||
|
const char *_mq_admin_secret_file = MQ_ADMIN_SECRET_FILE_DEFAULT;
|
||||||
|
|
||||||
|
// Check if the user is valid
|
||||||
|
int simpleauth_authenticate_user(const char *const user, const char *const password)
|
||||||
|
{
|
||||||
|
int result = -1;
|
||||||
|
|
||||||
|
if (simpleauth_valid_user(user))
|
||||||
|
{
|
||||||
|
char *pwd = get_secret_for_user(user);
|
||||||
|
if (pwd != NULL)
|
||||||
|
{
|
||||||
|
if (strcmp(pwd, password) == 0)
|
||||||
|
{
|
||||||
|
log_debugf("Correct password supplied. user=%s", user);
|
||||||
|
result = SIMPLEAUTH_VALID;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
log_debugf("Incorrect password supplied. user=%s", user);
|
||||||
|
result = SIMPLEAUTH_INVALID_PASSWORD;
|
||||||
|
}
|
||||||
|
memset(pwd, 0, strlen(pwd));
|
||||||
|
free(pwd);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
log_debugf("Failed to get secret for user '%s'", user);
|
||||||
|
result = SIMPLEAUTH_INVALID_PASSWORD;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
log_debugf("User does not exist. user=%s", user);
|
||||||
|
result = SIMPLEAUTH_INVALID_USER;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool simpleauth_valid_user(const char *const user)
|
||||||
|
{
|
||||||
|
bool valid = false;
|
||||||
|
if ((strcmp(user, APP_USER_NAME) == 0 || strcmp(user, ADMIN_USER_NAME) == 0))
|
||||||
|
{
|
||||||
|
valid = true;
|
||||||
|
}
|
||||||
|
return valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get_secret_for_user will return a char* containing the credential for the given user
|
||||||
|
* the credential is read from the filesystem if the relevant file exists and an environment
|
||||||
|
* variable if not
|
||||||
|
*
|
||||||
|
* The caller is responsible for clearing then freeing memory
|
||||||
|
*/
|
||||||
|
char *get_secret_for_user(const char *const user)
|
||||||
|
{
|
||||||
|
if (0 == strcmp(user, APP_USER_NAME))
|
||||||
|
{
|
||||||
|
char *secret = read_secret(_mq_app_secret_file);
|
||||||
|
if (secret != NULL)
|
||||||
|
{
|
||||||
|
return secret;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const char *pwdFromEnv = getenv("MQ_APP_PASSWORD");
|
||||||
|
if (pwdFromEnv != NULL)
|
||||||
|
{
|
||||||
|
log_infof("Environment variable MQ_APP_PASSWORD is deprecated, use secrets to set the passwords");
|
||||||
|
}
|
||||||
|
return strdup(pwdFromEnv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (0 == strcmp(user, ADMIN_USER_NAME))
|
||||||
|
{
|
||||||
|
char *secret = read_secret(_mq_admin_secret_file);
|
||||||
|
if (secret != NULL)
|
||||||
|
{
|
||||||
|
return secret;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const char *pwdFromEnv = getenv("MQ_ADMIN_PASSWORD");
|
||||||
|
if (pwdFromEnv != NULL)
|
||||||
|
{
|
||||||
|
log_infof("Environment variable MQ_ADMIN_PASSWORD is deprecated, use secrets to set the passwords");
|
||||||
|
}
|
||||||
|
return strdup(pwdFromEnv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* read_secret will return a char* containing the credential read from the filesystem for the given user
|
||||||
|
*
|
||||||
|
* The caller is responsible for clearing then freeing memory
|
||||||
|
*/
|
||||||
|
char *read_secret(const char *const secret)
|
||||||
|
{
|
||||||
|
FILE *fp = fopen(secret, "r");
|
||||||
|
if (fp)
|
||||||
|
{
|
||||||
|
const int line_size = MAX_PASSWORD_LENGTH + 1;
|
||||||
|
char *pwd = malloc(line_size);
|
||||||
|
char *result;
|
||||||
|
result = fgets(pwd, line_size, fp);
|
||||||
|
fclose(fp);
|
||||||
|
if (result == NULL)
|
||||||
|
{
|
||||||
|
memset(pwd, 0, line_size);
|
||||||
|
free(pwd);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
result[strcspn(result, "\r\n")] = 0;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
62
authservice/mqsimpleauth/src/simpleauth.h
Normal file
62
authservice/mqsimpleauth/src/simpleauth.h
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
© Copyright IBM Corporation 2021, 2024
|
||||||
|
|
||||||
|
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 _SIMPLEAUTH_H
|
||||||
|
#define _SIMPLEAUTH_H
|
||||||
|
|
||||||
|
#define SIMPLEAUTH_VALID 0
|
||||||
|
#define SIMPLEAUTH_INVALID_USER 1
|
||||||
|
#define SIMPLEAUTH_INVALID_PASSWORD 2
|
||||||
|
#define MQ_APP_SECRET_FILE_DEFAULT "/run/secrets/mqAppPassword"
|
||||||
|
#define MQ_ADMIN_SECRET_FILE_DEFAULT "/run/secrets/mqAdminPassword"
|
||||||
|
#define APP_USER_NAME "app"
|
||||||
|
#define ADMIN_USER_NAME "admin"
|
||||||
|
#define MAX_PASSWORD_LENGTH 256
|
||||||
|
|
||||||
|
extern const char *_mq_app_secret_file;
|
||||||
|
extern const char *_mq_admin_secret_file;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Authenticate a user, based on the supplied file name.
|
||||||
|
*
|
||||||
|
* @param user the user name to authenticate
|
||||||
|
* @param password the password of the user
|
||||||
|
* @return SIMPLEAUTH_VALID, SIMPLEAUTH_INVALID_USER or SIMPLEAUTH_INVALID_PASSWORD
|
||||||
|
*/
|
||||||
|
int simpleauth_authenticate_user(const char *const user, const char *const password);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate that a user exists in the password file.
|
||||||
|
*
|
||||||
|
* @param user the user name to validate
|
||||||
|
*/
|
||||||
|
bool simpleauth_valid_user(const char *const user);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the secret of the UserId.
|
||||||
|
*
|
||||||
|
* @param user the user name to validate
|
||||||
|
*/
|
||||||
|
char *get_secret_for_user(const char *const user);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the secret of the UserId.
|
||||||
|
*
|
||||||
|
* @param secret path for the secret file
|
||||||
|
*/
|
||||||
|
char *read_secret(const char *const secret);
|
||||||
|
|
||||||
|
#endif
|
||||||
412
authservice/mqsimpleauth/src/simpleauth_test.c
Normal file
412
authservice/mqsimpleauth/src/simpleauth_test.c
Normal file
@@ -0,0 +1,412 @@
|
|||||||
|
/*
|
||||||
|
© Copyright IBM Corporation 2021, 2024
|
||||||
|
|
||||||
|
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 "simpleauth.h"
|
||||||
|
#include "simpleauth_test.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.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 test to read secret
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void test_read_secret_ok()
|
||||||
|
{
|
||||||
|
test_start();
|
||||||
|
char *pwd = read_secret("./src/mqAdminPassword");
|
||||||
|
char *password = "fred:$2y$05$3Fp9";
|
||||||
|
if (0 != strcmp(pwd, password))
|
||||||
|
{
|
||||||
|
printf("%s: pwd: '%s'; password: '%s'\n", __func__, pwd, password);
|
||||||
|
test_fail(__func__);
|
||||||
|
}
|
||||||
|
|
||||||
|
test_pass();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Simple tests for authentication
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void test_simpleauth_valid_user_app_valid()
|
||||||
|
{
|
||||||
|
test_start();
|
||||||
|
bool validUser = simpleauth_valid_user(APP_USER_NAME);
|
||||||
|
printf("%s: app - %d\n", __func__, validUser);
|
||||||
|
|
||||||
|
if (!validUser)
|
||||||
|
test_fail(__func__);
|
||||||
|
test_pass();
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_simpleauth_valid_user_admin_valid()
|
||||||
|
{
|
||||||
|
test_start();
|
||||||
|
bool validUser = simpleauth_valid_user(ADMIN_USER_NAME);
|
||||||
|
printf("%s: admin - %d\n", __func__, validUser);
|
||||||
|
if (!validUser)
|
||||||
|
test_fail(__func__);
|
||||||
|
test_pass();
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_simpleauth_valid_user_george_invalid()
|
||||||
|
{
|
||||||
|
test_start();
|
||||||
|
bool validUser = simpleauth_valid_user("george");
|
||||||
|
printf("%s: george - %d\n", __func__, validUser);
|
||||||
|
if (validUser)
|
||||||
|
test_fail(__func__);
|
||||||
|
test_pass();
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_simpleauth_authenticate_user_fred_unknown()
|
||||||
|
{
|
||||||
|
test_start();
|
||||||
|
test_set_app_password_env("passw0rd-fred-env");
|
||||||
|
int rc = simpleauth_authenticate_user("fred", "passw0rd-fred-env");
|
||||||
|
printf("%s: fred - %d\n", __func__, rc);
|
||||||
|
if (rc != SIMPLEAUTH_INVALID_USER)
|
||||||
|
test_fail(__func__);
|
||||||
|
test_pass();
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_simpleauth_authenticate_user_app_ok()
|
||||||
|
{
|
||||||
|
test_start();
|
||||||
|
test_set_app_password_env("passw0rd-app-env");
|
||||||
|
int rc = simpleauth_authenticate_user("app", "passw0rd-app-env");
|
||||||
|
printf("%s: app - %d\n", __func__, rc);
|
||||||
|
if (rc != SIMPLEAUTH_VALID)
|
||||||
|
test_fail(__func__);
|
||||||
|
test_pass();
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_simpleauth_authenticate_user_admin_ok()
|
||||||
|
{
|
||||||
|
test_start();
|
||||||
|
test_set_admin_password_env("passw0rd-admin-env");
|
||||||
|
int rc = simpleauth_authenticate_user("admin", "passw0rd-admin-env");
|
||||||
|
printf("%s: admin - %d\n", __func__, rc);
|
||||||
|
if (rc != SIMPLEAUTH_VALID)
|
||||||
|
test_fail(__func__);
|
||||||
|
test_pass();
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_simpleauth_authenticate_user_admin_invalidpasswords()
|
||||||
|
{
|
||||||
|
test_start();
|
||||||
|
test_set_admin_password_env("password-admin-env");
|
||||||
|
const char *bad_passwords[] = {
|
||||||
|
"",
|
||||||
|
"passw0rd-admin-env",
|
||||||
|
"Password-admin-env",
|
||||||
|
"pass",
|
||||||
|
"password",
|
||||||
|
"password-app",
|
||||||
|
"password-app-env",
|
||||||
|
"password-admin-env-123"};
|
||||||
|
size_t bad_pass_len = sizeof(bad_passwords) / sizeof(bad_passwords[0]);
|
||||||
|
|
||||||
|
for (int i = 0; i < bad_pass_len; i++)
|
||||||
|
{
|
||||||
|
int rc = simpleauth_authenticate_user("admin", bad_passwords[i]);
|
||||||
|
printf("%s: admin/%s - %d\n", __func__, bad_passwords[i], rc);
|
||||||
|
if (rc != SIMPLEAUTH_INVALID_PASSWORD)
|
||||||
|
test_fail(__func__);
|
||||||
|
test_pass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_simpleauth_authenticate_user_admin_secret_file_valid()
|
||||||
|
{
|
||||||
|
test_start();
|
||||||
|
test_set_admin_password_file("password-admin-file");
|
||||||
|
int rc = simpleauth_authenticate_user("admin", "password-admin-file");
|
||||||
|
printf("%s: admin - %d\n", __func__, rc);
|
||||||
|
if (rc != SIMPLEAUTH_VALID)
|
||||||
|
test_fail(__func__);
|
||||||
|
test_pass();
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_simpleauth_authenticate_user_admin_secret_file_long()
|
||||||
|
{
|
||||||
|
test_start();
|
||||||
|
|
||||||
|
const int test_password_length = MAX_PASSWORD_LENGTH + 7;
|
||||||
|
char test_password[test_password_length];
|
||||||
|
char truncated_password[MAX_PASSWORD_LENGTH + 1];
|
||||||
|
for (int i = 0; i < test_password_length; i++)
|
||||||
|
{
|
||||||
|
test_password[i] = '0' + ((i + 1) % 10);
|
||||||
|
if (i < MAX_PASSWORD_LENGTH)
|
||||||
|
{
|
||||||
|
truncated_password[i] = test_password[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
test_password[test_password_length] = 0;
|
||||||
|
truncated_password[MAX_PASSWORD_LENGTH] = 0;
|
||||||
|
test_set_admin_password_file(test_password);
|
||||||
|
|
||||||
|
int rc = simpleauth_authenticate_user("admin", test_password);
|
||||||
|
if (rc != SIMPLEAUTH_INVALID_PASSWORD)
|
||||||
|
{
|
||||||
|
printf("%s: admin/'%s' - %d\n", __func__, test_password, rc);
|
||||||
|
test_fail(__func__);
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = simpleauth_authenticate_user("admin", truncated_password);
|
||||||
|
if (rc != SIMPLEAUTH_VALID)
|
||||||
|
{
|
||||||
|
printf("%s: admin/'%s' - %d\n", __func__, truncated_password, rc);
|
||||||
|
test_fail(__func__);
|
||||||
|
}
|
||||||
|
|
||||||
|
test_pass();
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_simpleauth_authenticate_user_admin_secret_file_invalid()
|
||||||
|
{
|
||||||
|
test_start();
|
||||||
|
|
||||||
|
test_set_admin_password_file("password-admin-file");
|
||||||
|
const char *bad_passwords[] = {
|
||||||
|
"",
|
||||||
|
"passw0rd-admin-file",
|
||||||
|
"Password-admin-file",
|
||||||
|
"pass",
|
||||||
|
"password",
|
||||||
|
"password-app-file",
|
||||||
|
"password-admin-file-123"};
|
||||||
|
size_t bad_pass_len = sizeof(bad_passwords) / sizeof(bad_passwords[0]);
|
||||||
|
|
||||||
|
for (int i = 0; i < bad_pass_len; i++)
|
||||||
|
{
|
||||||
|
int rc = simpleauth_authenticate_user("admin", bad_passwords[i]);
|
||||||
|
printf("%s: admin/%s - %d\n", __func__, bad_passwords[i], rc);
|
||||||
|
if (rc != SIMPLEAUTH_INVALID_PASSWORD)
|
||||||
|
test_fail(__func__);
|
||||||
|
test_pass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_simpleauth_authenticate_user_app_secret_file_valid()
|
||||||
|
{
|
||||||
|
test_start();
|
||||||
|
test_set_app_password_file("password-app-file");
|
||||||
|
int rc = simpleauth_authenticate_user("app", "password-app-file");
|
||||||
|
printf("%s: admin - %d\n", __func__, rc);
|
||||||
|
if (rc != SIMPLEAUTH_VALID)
|
||||||
|
test_fail(__func__);
|
||||||
|
test_pass();
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_simpleauth_authenticate_user_app_secret_file_invalid()
|
||||||
|
{
|
||||||
|
test_start();
|
||||||
|
|
||||||
|
test_set_app_password_file("password-app-file");
|
||||||
|
const char *bad_passwords[] = {
|
||||||
|
"",
|
||||||
|
"passw0rd-app-file",
|
||||||
|
"Password-app-file",
|
||||||
|
"pass",
|
||||||
|
"password",
|
||||||
|
"password-admin-file",
|
||||||
|
"password-app-file-123"};
|
||||||
|
size_t bad_pass_len = sizeof(bad_passwords) / sizeof(bad_passwords[0]);
|
||||||
|
|
||||||
|
for (int i = 0; i < bad_pass_len; i++)
|
||||||
|
{
|
||||||
|
int rc = simpleauth_authenticate_user("app", bad_passwords[i]);
|
||||||
|
printf("%s: app/%s - %d\n", __func__, bad_passwords[i], rc);
|
||||||
|
if (rc != SIMPLEAUTH_INVALID_PASSWORD)
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
test_set_admin_password_env("passw0rd");
|
||||||
|
test_set_app_password_env("passw0rd");
|
||||||
|
for (int i = 0; i < NUM_TESTS_PER_THREAD; i++)
|
||||||
|
{
|
||||||
|
int rc = simpleauth_authenticate_user("admin", "passw0rd");
|
||||||
|
if (rc != SIMPLEAUTH_VALID)
|
||||||
|
test_fail(__func__);
|
||||||
|
rc = simpleauth_authenticate_user("app", "passw0rd");
|
||||||
|
if (rc != SIMPLEAUTH_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_simpleauth_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();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Test utility functions
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
int write_secret(const char *const secretFile, const char *const value)
|
||||||
|
{
|
||||||
|
FILE *fp = fopen(secretFile, "w");
|
||||||
|
if (fp)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
rc = fprintf(fp, "%s\n", value);
|
||||||
|
fclose(fp);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_set_admin_password_env(const char *const password)
|
||||||
|
{
|
||||||
|
setenv("MQ_ADMIN_PASSWORD", password, 1);
|
||||||
|
_mq_admin_secret_file = MQ_ADMIN_SECRET_FILE_DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_set_app_password_env(const char *const password)
|
||||||
|
{
|
||||||
|
setenv("MQ_APP_PASSWORD", password, 1);
|
||||||
|
_mq_app_secret_file = MQ_APP_SECRET_FILE_DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_set_admin_password_file(const char *const password)
|
||||||
|
{
|
||||||
|
write_secret(MQ_ADMIN_SECRET_FILE_TEST, password);
|
||||||
|
_mq_admin_secret_file = MQ_ADMIN_SECRET_FILE_TEST;
|
||||||
|
unsetenv("MQ_ADMIN_PASSWORD");
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_set_app_password_file(const char *const password)
|
||||||
|
{
|
||||||
|
write_secret(MQ_APP_SECRET_FILE_TEST, password);
|
||||||
|
_mq_app_secret_file = MQ_APP_SECRET_FILE_TEST;
|
||||||
|
unsetenv("MQ_APP_PASSWORD");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
// Turn on debugging for the tests
|
||||||
|
setenv("DEBUG", "true", true);
|
||||||
|
log_init("simpleauth_test.log");
|
||||||
|
|
||||||
|
test_read_secret_ok();
|
||||||
|
test_simpleauth_valid_user_app_valid();
|
||||||
|
test_simpleauth_valid_user_admin_valid();
|
||||||
|
test_simpleauth_valid_user_george_invalid();
|
||||||
|
test_simpleauth_authenticate_user_fred_unknown();
|
||||||
|
test_simpleauth_authenticate_user_app_ok();
|
||||||
|
test_simpleauth_authenticate_user_admin_ok();
|
||||||
|
test_simpleauth_authenticate_user_admin_invalidpasswords();
|
||||||
|
test_simpleauth_authenticate_user_admin_secret_file_valid();
|
||||||
|
test_simpleauth_authenticate_user_admin_secret_file_long();
|
||||||
|
test_simpleauth_authenticate_user_admin_secret_file_invalid();
|
||||||
|
test_simpleauth_authenticate_user_app_secret_file_valid();
|
||||||
|
test_simpleauth_authenticate_user_app_secret_file_invalid();
|
||||||
|
|
||||||
|
log_close();
|
||||||
|
|
||||||
|
// Call multi-threaded test last, because it re-initializes the log to use a file
|
||||||
|
test_simpleauth_authenticate_user_multithreaded("simpleauth_test_multithreaded.log");
|
||||||
|
}
|
||||||
28
authservice/mqsimpleauth/src/simpleauth_test.h
Normal file
28
authservice/mqsimpleauth/src/simpleauth_test.h
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
© Copyright IBM Corporation 2024
|
||||||
|
|
||||||
|
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 _SIMPLEAUTH_TEST_H
|
||||||
|
#define _SIMPLEAUTH_TEST_H
|
||||||
|
|
||||||
|
#define MQ_ADMIN_SECRET_FILE_TEST "testSecretAdmin"
|
||||||
|
#define MQ_APP_SECRET_FILE_TEST "testSecretApp"
|
||||||
|
|
||||||
|
void test_set_admin_password_env(const char *const password);
|
||||||
|
void test_set_admin_password_file(const char *const password);
|
||||||
|
void test_set_app_password_env(const char *const password);
|
||||||
|
void test_set_app_password_file(const char *const password);
|
||||||
|
|
||||||
|
#endif
|
||||||
77
cmd/chkmqhealthy/main.go
Normal file
77
cmd/chkmqhealthy/main.go
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
© Copyright IBM Corporation 2017, 2024
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// chkmqhealthy checks that MQ is healthy, by checking the output of the "dspmq" command
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"os/signal"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/ibm-messaging/mq-container/pkg/name"
|
||||||
|
)
|
||||||
|
|
||||||
|
func queueManagerHealthy(ctx context.Context) (bool, error) {
|
||||||
|
name, err := name.GetQueueManagerName()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
// Specify the queue manager name, just in case someone's created a second queue manager
|
||||||
|
// #nosec G204
|
||||||
|
cmd := exec.CommandContext(ctx, "dspmq", "-n", "-m", name)
|
||||||
|
// Run the command and wait for completion
|
||||||
|
out, err := cmd.CombinedOutput()
|
||||||
|
fmt.Printf("%s", out)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
readyStrings := []string{
|
||||||
|
"(RUNNING)",
|
||||||
|
"(RUNNING AS STANDBY)",
|
||||||
|
"(RECOVERY GROUP LEADER)",
|
||||||
|
"(STARTING)",
|
||||||
|
"(REPLICA)",
|
||||||
|
}
|
||||||
|
for _, checkString := range readyStrings {
|
||||||
|
if strings.Contains(string(out), checkString) {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func doMain() int {
|
||||||
|
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, os.Kill)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
healthy, err := queueManagerHealthy(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
if !healthy {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
os.Exit(doMain())
|
||||||
|
}
|
||||||
88
cmd/chkmqready/main.go
Normal file
88
cmd/chkmqready/main.go
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
/*
|
||||||
|
© Copyright IBM Corporation 2017, 2024
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// chkmqready checks that MQ is ready for work, by checking if the MQ listener port is available
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
|
||||||
|
"github.com/ibm-messaging/mq-container/internal/ready"
|
||||||
|
"github.com/ibm-messaging/mq-container/pkg/name"
|
||||||
|
)
|
||||||
|
|
||||||
|
func doMain() int {
|
||||||
|
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, os.Kill)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
// Check if runmqserver has indicated that it's finished configuration
|
||||||
|
r, err := ready.Check()
|
||||||
|
if !r || err != nil {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
name, err := name.GetQueueManagerName()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the queue manager has a running listener
|
||||||
|
status, err := ready.Status(ctx, name)
|
||||||
|
if err != nil {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
switch status {
|
||||||
|
case ready.StatusActiveQM:
|
||||||
|
portOpen, err := checkPort("127.0.0.1:1414")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
if !portOpen {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
case ready.StatusRecoveryQM:
|
||||||
|
fmt.Printf("Detected queue manager running as recovery leader")
|
||||||
|
return 0
|
||||||
|
case ready.StatusStandbyQM:
|
||||||
|
fmt.Printf("Detected queue manager running in standby mode")
|
||||||
|
return 10
|
||||||
|
case ready.StatusReplicaQM:
|
||||||
|
fmt.Printf("Detected queue manager running in replica mode")
|
||||||
|
return 20
|
||||||
|
default:
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
os.Exit(doMain())
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkPort(address string) (portOpen bool, err error) {
|
||||||
|
var conn net.Conn
|
||||||
|
conn, err = net.Dial("tcp", address)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
portOpen = true
|
||||||
|
err = conn.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
167
cmd/chkmqstarted/main.go
Normal file
167
cmd/chkmqstarted/main.go
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
/*
|
||||||
|
© Copyright IBM Corporation 2021, 2024
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// chkmqstarted checks that MQ has successfully started, by checking the output of the "dspmq" command
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"os/signal"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ibm-messaging/mq-container/internal/ready"
|
||||||
|
"github.com/ibm-messaging/mq-container/pkg/name"
|
||||||
|
)
|
||||||
|
|
||||||
|
func queueManagerStarted(ctx context.Context) (bool, error) {
|
||||||
|
|
||||||
|
name, err := name.GetQueueManagerName()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
readyStrings := []string{
|
||||||
|
"(RUNNING)",
|
||||||
|
"(RUNNING AS STANDBY)",
|
||||||
|
"(RECOVERY GROUP LEADER)",
|
||||||
|
"(STARTING)",
|
||||||
|
"(REPLICA)",
|
||||||
|
}
|
||||||
|
|
||||||
|
// For Native-HA only, check if the queue manager instance is in-sync with one or more replicas
|
||||||
|
// - If not in-sync within the expected time period, revert to checking on queue manager 'ready' status
|
||||||
|
// - This ensures we do not block indefinitely for breaking changes (i.e. protocol changes)
|
||||||
|
if os.Getenv("MQ_NATIVE_HA") == "true" {
|
||||||
|
|
||||||
|
// Check if the Native-HA queue manager instance is currently in-sync
|
||||||
|
isReadyToSync, isInSync, err := isInSyncWithReplicas(ctx, name, readyStrings)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
} else if isInSync {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the Native-HA queue manager instance is ready-to-sync
|
||||||
|
// - A successful queue manager 'ready' status indicates that we are ready-to-sync
|
||||||
|
if !isReadyToSync {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
err = ready.SetReadyToSync()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the time period for checking in-sync has now expired
|
||||||
|
// - We have already confirmed a successful queue manager 'ready' status
|
||||||
|
// - Therefore the expiration of the in-sync time period will result in success
|
||||||
|
expired, err := hasInSyncTimePeriodExpired()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
} else if expired {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Specify the queue manager name, just in case someone's created a second queue manager
|
||||||
|
// #nosec G204
|
||||||
|
cmd := exec.CommandContext(ctx, "dspmq", "-n", "-m", name)
|
||||||
|
// Run the command and wait for completion
|
||||||
|
out, err := cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, checkString := range readyStrings {
|
||||||
|
if strings.Contains(string(out), checkString) {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// isInSyncWithReplicas returns the in-sync status for a Native-HA queue manager instance
|
||||||
|
func isInSyncWithReplicas(ctx context.Context, name string, readyStrings []string) (bool, bool, error) {
|
||||||
|
|
||||||
|
cmd := exec.CommandContext(ctx, "dspmq", "-n", "-o", "nativeha", "-m", name)
|
||||||
|
out, err := cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
return false, false, err
|
||||||
|
} else if strings.Contains(string(out), "INSYNC(YES)") {
|
||||||
|
return true, true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, checkString := range readyStrings {
|
||||||
|
if strings.Contains(string(out), checkString) {
|
||||||
|
return true, false, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// hasInSyncTimePeriodExpired returns true if a Native-HA queue manager instance is not in-sync within the expected time period, otherwise false
|
||||||
|
func hasInSyncTimePeriodExpired() (bool, error) {
|
||||||
|
|
||||||
|
// Default timeout 5 seconds
|
||||||
|
var timeout int64 = 5
|
||||||
|
var err error
|
||||||
|
|
||||||
|
// Check if a timeout override has been set
|
||||||
|
customTimeout := os.Getenv("MQ_NATIVE_HA_IN_SYNC_TIMEOUT")
|
||||||
|
if customTimeout != "" {
|
||||||
|
timeout, err = strconv.ParseInt(customTimeout, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isReadyToSync, readyToSyncStartTime, err := ready.GetReadyToSyncStartTime()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if isReadyToSync && time.Now().Unix()-readyToSyncStartTime.Unix() >= timeout {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func doMain() int {
|
||||||
|
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, os.Kill)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
started, err := queueManagerStarted(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
if !started {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
os.Exit(doMain())
|
||||||
|
}
|
||||||
179
cmd/runmqdevserver/main.go
Normal file
179
cmd/runmqdevserver/main.go
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
/*
|
||||||
|
© Copyright IBM Corporation 2018, 2023
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/ibm-messaging/mq-container/internal/copy"
|
||||||
|
"github.com/ibm-messaging/mq-container/internal/simpleauth"
|
||||||
|
"github.com/ibm-messaging/mq-container/pkg/containerruntimelogger"
|
||||||
|
"github.com/ibm-messaging/mq-container/pkg/logger"
|
||||||
|
"github.com/ibm-messaging/mq-container/pkg/name"
|
||||||
|
)
|
||||||
|
|
||||||
|
var log *logger.Logger
|
||||||
|
|
||||||
|
func getLogFormat() string {
|
||||||
|
logFormat := strings.ToLower(strings.TrimSpace(os.Getenv("MQ_LOGGING_CONSOLE_FORMAT")))
|
||||||
|
//old-style env var is used.
|
||||||
|
if logFormat == "" {
|
||||||
|
logFormat = strings.ToLower(strings.TrimSpace(os.Getenv("LOG_FORMAT")))
|
||||||
|
}
|
||||||
|
|
||||||
|
if logFormat != "" && (logFormat == "basic" || logFormat == "json") {
|
||||||
|
return logFormat
|
||||||
|
} else {
|
||||||
|
//this is the case where value is either empty string or set to something other than "basic"/"json"
|
||||||
|
logFormat = "basic"
|
||||||
|
}
|
||||||
|
|
||||||
|
return logFormat
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDebug() bool {
|
||||||
|
debug := os.Getenv("DEBUG")
|
||||||
|
if debug == "true" || debug == "1" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func configureLogger() error {
|
||||||
|
var err error
|
||||||
|
f := getLogFormat()
|
||||||
|
d := getDebug()
|
||||||
|
n, err := name.GetQueueManagerName()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
switch f {
|
||||||
|
case "json":
|
||||||
|
log, err = logger.NewLogger(os.Stderr, d, true, n)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case "basic":
|
||||||
|
log, err = logger.NewLogger(os.Stderr, d, false, n)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
log, err = logger.NewLogger(os.Stdout, d, false, n)
|
||||||
|
return fmt.Errorf("invalid value for LOG_FORMAT: %v", f)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func logTerminationf(format string, args ...interface{}) {
|
||||||
|
logTermination(fmt.Sprintf(format, args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Duplicated code
|
||||||
|
func logTermination(args ...interface{}) {
|
||||||
|
msg := fmt.Sprint(args...)
|
||||||
|
// Write the message to the termination log. This is not the default place
|
||||||
|
// that Kubernetes will look for termination information.
|
||||||
|
log.Debugf("Writing termination message: %v", msg)
|
||||||
|
// #nosec G306 - its a read by owner/s group, and pose no harm.
|
||||||
|
err := os.WriteFile("/run/termination-log", []byte(msg), 0660)
|
||||||
|
if err != nil {
|
||||||
|
log.Debug(err)
|
||||||
|
}
|
||||||
|
log.Error(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func doMain() error {
|
||||||
|
err := configureLogger()
|
||||||
|
if err != nil {
|
||||||
|
logTermination(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = containerruntimelogger.LogContainerDetails(log)
|
||||||
|
if err != nil {
|
||||||
|
logTermination(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialise 10-dev.mqsc file on ephemeral volume
|
||||||
|
// #nosec G306 - its a read by owner/s group, and pose no harm.
|
||||||
|
err = os.WriteFile("/run/10-dev.mqsc", []byte(""), 0660)
|
||||||
|
if err != nil {
|
||||||
|
logTermination(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialise 20-dev-tls.mqsc file on ephemeral volume
|
||||||
|
// #nosec G306 - its a read by owner/s group, and pose no harm.
|
||||||
|
err = os.WriteFile("/run/20-dev-tls.mqsc", []byte(""), 0660)
|
||||||
|
if err != nil {
|
||||||
|
logTermination(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialise /run/qm-service-component.ini file on ephemeral volume
|
||||||
|
// #nosec G306 - its a read by owner/s group, and pose no harm.
|
||||||
|
err = os.WriteFile("/run/qm-service-component.ini", []byte(""), 0660)
|
||||||
|
if err != nil {
|
||||||
|
logTermination(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable mq simpleauth if MQ_CONNAUTH_USE_HTP is set true
|
||||||
|
// and either or both of MQ_APP_PASSWORD and MQ_ADMIN_PASSWORD
|
||||||
|
// environment variables specified.
|
||||||
|
enableHtPwd, set := os.LookupEnv("MQ_CONNAUTH_USE_HTP")
|
||||||
|
if set && strings.EqualFold(enableHtPwd, "true") {
|
||||||
|
err := copy.CopyFile("/etc/mqm/qm-service-component.ini.default", "/run/qm-service-component.ini")
|
||||||
|
if err != nil {
|
||||||
|
logTermination(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = simpleauth.CheckForPasswords(log)
|
||||||
|
if err != nil {
|
||||||
|
logTermination(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = updateMQSC(set)
|
||||||
|
if err != nil {
|
||||||
|
logTerminationf("Error updating MQSC: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var osExit = os.Exit
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
err := doMain()
|
||||||
|
if err != nil {
|
||||||
|
osExit(1)
|
||||||
|
} else {
|
||||||
|
// Replace this process with runmqserver
|
||||||
|
// #nosec G204
|
||||||
|
err = syscall.Exec("/usr/local/bin/runmqserver", []string{"runmqserver", "-nologruntime", "-dev"}, os.Environ())
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Error replacing this process with runmqserver: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
45
cmd/runmqdevserver/mqsc.go
Normal file
45
cmd/runmqdevserver/mqsc.go
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
© Copyright IBM Corporation 2018, 2023
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/ibm-messaging/mq-container/internal/mqtemplate"
|
||||||
|
)
|
||||||
|
|
||||||
|
func updateMQSC(appPasswordRequired bool) error {
|
||||||
|
|
||||||
|
var checkClient string
|
||||||
|
if appPasswordRequired {
|
||||||
|
checkClient = "REQUIRED"
|
||||||
|
} else {
|
||||||
|
checkClient = "ASQMGR"
|
||||||
|
}
|
||||||
|
|
||||||
|
const mqscLink string = "/run/10-dev.mqsc"
|
||||||
|
const mqscTemplate string = "/etc/mqm/10-dev.mqsc.tpl"
|
||||||
|
|
||||||
|
if os.Getenv("MQ_DEV") == "true" {
|
||||||
|
// Re-configure channel if app password not set
|
||||||
|
err := mqtemplate.ProcessTemplateFile(mqscTemplate, mqscLink, map[string]string{"ChckClnt": checkClient}, log)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
57
cmd/runmqserver/crtmqvol.go
Normal file
57
cmd/runmqserver/crtmqvol.go
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
© Copyright IBM Corporation 2017, 2023
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
func createVolume(dataPath string) error {
|
||||||
|
_, err := os.Stat(dataPath)
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
// #nosec G301
|
||||||
|
err = os.MkdirAll(dataPath, 0755)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete files/directories from specified path
|
||||||
|
func cleanVolume(cleanPath string) error {
|
||||||
|
// #nosec G304
|
||||||
|
dirContents, err := os.ReadDir(cleanPath)
|
||||||
|
if err != nil {
|
||||||
|
if !os.IsNotExist(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, name := range dirContents {
|
||||||
|
err = os.RemoveAll(filepath.Join(cleanPath, name.Name()))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
92
cmd/runmqserver/license.go
Normal file
92
cmd/runmqserver/license.go
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
/*
|
||||||
|
© Copyright IBM Corporation 2017, 2023
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// resolveLicenseFile returns the file name of the MQ license file, taking into
|
||||||
|
// account the language set by the LANG environment variable
|
||||||
|
func resolveLicenseFile() string {
|
||||||
|
lang, ok := os.LookupEnv("LANG")
|
||||||
|
if !ok {
|
||||||
|
return "English.txt"
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case strings.HasPrefix(lang, "zh_TW"):
|
||||||
|
return "Chinese_TW.txt"
|
||||||
|
case strings.HasPrefix(lang, "zh"):
|
||||||
|
return "Chinese.txt"
|
||||||
|
// Differentiate Czech (cs) and Kashubian (csb)
|
||||||
|
case strings.HasPrefix(lang, "cs") && !strings.HasPrefix(lang, "csb"):
|
||||||
|
return "Czech.txt"
|
||||||
|
case strings.HasPrefix(lang, "fr"):
|
||||||
|
return "French.txt"
|
||||||
|
case strings.HasPrefix(lang, "de"):
|
||||||
|
return "German.txt"
|
||||||
|
case strings.HasPrefix(lang, "el"):
|
||||||
|
return "Greek.txt"
|
||||||
|
case strings.HasPrefix(lang, "id"):
|
||||||
|
return "Indonesian.txt"
|
||||||
|
case strings.HasPrefix(lang, "it"):
|
||||||
|
return "Italian.txt"
|
||||||
|
case strings.HasPrefix(lang, "ja"):
|
||||||
|
return "Japanese.txt"
|
||||||
|
// Differentiate Korean (ko) from Konkani (kok)
|
||||||
|
case strings.HasPrefix(lang, "ko") && !strings.HasPrefix(lang, "kok"):
|
||||||
|
return "Korean.txt"
|
||||||
|
case strings.HasPrefix(lang, "lt"):
|
||||||
|
return "Lithuanian.txt"
|
||||||
|
case strings.HasPrefix(lang, "pl"):
|
||||||
|
return "Polish.txt"
|
||||||
|
case strings.HasPrefix(lang, "pt"):
|
||||||
|
return "Portugese.txt"
|
||||||
|
case strings.HasPrefix(lang, "ru"):
|
||||||
|
return "Russian.txt"
|
||||||
|
case strings.HasPrefix(lang, "sl"):
|
||||||
|
return "Slovenian.txt"
|
||||||
|
case strings.HasPrefix(lang, "es"):
|
||||||
|
return "Spanish.txt"
|
||||||
|
case strings.HasPrefix(lang, "tr"):
|
||||||
|
return "Turkish.txt"
|
||||||
|
}
|
||||||
|
return "English.txt"
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkLicense() (bool, error) {
|
||||||
|
lic, ok := os.LookupEnv("LICENSE")
|
||||||
|
switch {
|
||||||
|
case ok && lic == "accept":
|
||||||
|
return true, nil
|
||||||
|
case ok && lic == "view":
|
||||||
|
file := filepath.Join("/opt/mqm/licenses", resolveLicenseFile())
|
||||||
|
// #nosec G304
|
||||||
|
buf, err := os.ReadFile(file)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
log.Println(string(buf))
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
log.Println("Error: Set environment variable LICENSE=accept to indicate acceptance of license terms and conditions.")
|
||||||
|
log.Println("License agreements and information can be viewed by setting the environment variable LICENSE=view. You can also set the LANG environment variable to view the license in a different language.")
|
||||||
|
return false, errors.New("Set environment variable LICENSE=accept to indicate acceptance of license terms and conditions")
|
||||||
|
}
|
||||||
282
cmd/runmqserver/license_test.go
Normal file
282
cmd/runmqserver/license_test.go
Normal file
@@ -0,0 +1,282 @@
|
|||||||
|
/*
|
||||||
|
© Copyright IBM Corporation 2017
|
||||||
|
|
||||||
|
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 (
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var licenseTests = []struct {
|
||||||
|
in string
|
||||||
|
out string
|
||||||
|
}{
|
||||||
|
{"en_US.UTF_8", "English.txt"},
|
||||||
|
{"en_US.ISO-8859-15", "English.txt"},
|
||||||
|
{"es_GB", "Spanish.txt"},
|
||||||
|
{"el_ES.UTF_8", "Greek.txt"},
|
||||||
|
// Cover a wide variety of valid values
|
||||||
|
{"af", "English.txt"},
|
||||||
|
{"af_ZA", "English.txt"},
|
||||||
|
{"ar", "English.txt"},
|
||||||
|
{"ar_AE", "English.txt"},
|
||||||
|
{"ar_BH", "English.txt"},
|
||||||
|
{"ar_DZ", "English.txt"},
|
||||||
|
{"ar_EG", "English.txt"},
|
||||||
|
{"ar_IQ", "English.txt"},
|
||||||
|
{"ar_JO", "English.txt"},
|
||||||
|
{"ar_KW", "English.txt"},
|
||||||
|
{"ar_LB", "English.txt"},
|
||||||
|
{"ar_LY", "English.txt"},
|
||||||
|
{"ar_MA", "English.txt"},
|
||||||
|
{"ar_OM", "English.txt"},
|
||||||
|
{"ar_QA", "English.txt"},
|
||||||
|
{"ar_SA", "English.txt"},
|
||||||
|
{"ar_SY", "English.txt"},
|
||||||
|
{"ar_TN", "English.txt"},
|
||||||
|
{"ar_YE", "English.txt"},
|
||||||
|
{"az", "English.txt"},
|
||||||
|
{"az_AZ", "English.txt"},
|
||||||
|
{"az_AZ", "English.txt"},
|
||||||
|
{"be", "English.txt"},
|
||||||
|
{"be_BY", "English.txt"},
|
||||||
|
{"bg", "English.txt"},
|
||||||
|
{"bg_BG", "English.txt"},
|
||||||
|
{"bs_BA", "English.txt"},
|
||||||
|
{"ca", "English.txt"},
|
||||||
|
{"ca_ES", "English.txt"},
|
||||||
|
{"cs", "Czech.txt"},
|
||||||
|
{"cs_CZ", "Czech.txt"},
|
||||||
|
{"csb_PL", "English.txt"},
|
||||||
|
{"cy", "English.txt"},
|
||||||
|
{"cy_GB", "English.txt"},
|
||||||
|
{"da", "English.txt"},
|
||||||
|
{"da_DK", "English.txt"},
|
||||||
|
{"de", "German.txt"},
|
||||||
|
{"de_AT", "German.txt"},
|
||||||
|
{"de_CH", "German.txt"},
|
||||||
|
{"de_DE", "German.txt"},
|
||||||
|
{"de_LI", "German.txt"},
|
||||||
|
{"de_LU", "German.txt"},
|
||||||
|
{"dv", "English.txt"},
|
||||||
|
{"dv_MV", "English.txt"},
|
||||||
|
{"el", "Greek.txt"},
|
||||||
|
{"el_GR", "Greek.txt"},
|
||||||
|
{"en", "English.txt"},
|
||||||
|
{"en_AU", "English.txt"},
|
||||||
|
{"en_BZ", "English.txt"},
|
||||||
|
{"en_CA", "English.txt"},
|
||||||
|
{"en_CB", "English.txt"},
|
||||||
|
{"en_GB", "English.txt"},
|
||||||
|
{"en_IE", "English.txt"},
|
||||||
|
{"en_JM", "English.txt"},
|
||||||
|
{"en_NZ", "English.txt"},
|
||||||
|
{"en_PH", "English.txt"},
|
||||||
|
{"en_TT", "English.txt"},
|
||||||
|
{"en_US", "English.txt"},
|
||||||
|
{"en_ZA", "English.txt"},
|
||||||
|
{"en_ZW", "English.txt"},
|
||||||
|
{"eo", "English.txt"},
|
||||||
|
{"es", "Spanish.txt"},
|
||||||
|
{"es_AR", "Spanish.txt"},
|
||||||
|
{"es_BO", "Spanish.txt"},
|
||||||
|
{"es_CL", "Spanish.txt"},
|
||||||
|
{"es_CO", "Spanish.txt"},
|
||||||
|
{"es_CR", "Spanish.txt"},
|
||||||
|
{"es_DO", "Spanish.txt"},
|
||||||
|
{"es_EC", "Spanish.txt"},
|
||||||
|
{"es_ES", "Spanish.txt"},
|
||||||
|
{"es_ES", "Spanish.txt"},
|
||||||
|
{"es_GT", "Spanish.txt"},
|
||||||
|
{"es_HN", "Spanish.txt"},
|
||||||
|
{"es_MX", "Spanish.txt"},
|
||||||
|
{"es_NI", "Spanish.txt"},
|
||||||
|
{"es_PA", "Spanish.txt"},
|
||||||
|
{"es_PE", "Spanish.txt"},
|
||||||
|
{"es_PR", "Spanish.txt"},
|
||||||
|
{"es_PY", "Spanish.txt"},
|
||||||
|
{"es_SV", "Spanish.txt"},
|
||||||
|
{"es_UY", "Spanish.txt"},
|
||||||
|
{"es_VE", "Spanish.txt"},
|
||||||
|
{"et", "English.txt"},
|
||||||
|
{"et_EE", "English.txt"},
|
||||||
|
{"eu", "English.txt"},
|
||||||
|
{"eu_ES", "English.txt"},
|
||||||
|
{"fa", "English.txt"},
|
||||||
|
{"fa_IR", "English.txt"},
|
||||||
|
{"fi", "English.txt"},
|
||||||
|
{"fi_FI", "English.txt"},
|
||||||
|
{"fo", "English.txt"},
|
||||||
|
{"fo_FO", "English.txt"},
|
||||||
|
{"fr", "French.txt"},
|
||||||
|
{"fr_BE", "French.txt"},
|
||||||
|
{"fr_CA", "French.txt"},
|
||||||
|
{"fr_CH", "French.txt"},
|
||||||
|
{"fr_FR", "French.txt"},
|
||||||
|
{"fr_LU", "French.txt"},
|
||||||
|
{"fr_MC", "French.txt"},
|
||||||
|
{"gl", "English.txt"},
|
||||||
|
{"gl_ES", "English.txt"},
|
||||||
|
{"gu", "English.txt"},
|
||||||
|
{"gu_IN", "English.txt"},
|
||||||
|
{"he", "English.txt"},
|
||||||
|
{"he_IL", "English.txt"},
|
||||||
|
{"hi", "English.txt"},
|
||||||
|
{"hi_IN", "English.txt"},
|
||||||
|
{"hr", "English.txt"},
|
||||||
|
{"hr_BA", "English.txt"},
|
||||||
|
{"hr_HR", "English.txt"},
|
||||||
|
{"hu", "English.txt"},
|
||||||
|
{"hu_HU", "English.txt"},
|
||||||
|
{"hy", "English.txt"},
|
||||||
|
{"hy_AM", "English.txt"},
|
||||||
|
{"id", "Indonesian.txt"},
|
||||||
|
{"id_ID", "Indonesian.txt"},
|
||||||
|
{"is", "English.txt"},
|
||||||
|
{"is_IS", "English.txt"},
|
||||||
|
{"it", "Italian.txt"},
|
||||||
|
{"it_CH", "Italian.txt"},
|
||||||
|
{"it_IT", "Italian.txt"},
|
||||||
|
{"ja", "Japanese.txt"},
|
||||||
|
{"ja_JP", "Japanese.txt"},
|
||||||
|
{"ka", "English.txt"},
|
||||||
|
{"ka_GE", "English.txt"},
|
||||||
|
{"kk", "English.txt"},
|
||||||
|
{"kk_KZ", "English.txt"},
|
||||||
|
{"kn", "English.txt"},
|
||||||
|
{"kn_IN", "English.txt"},
|
||||||
|
{"ko", "Korean.txt"},
|
||||||
|
{"ko_KR", "Korean.txt"},
|
||||||
|
{"kok", "English.txt"},
|
||||||
|
{"kok_IN", "English.txt"},
|
||||||
|
{"ky", "English.txt"},
|
||||||
|
{"ky_KG", "English.txt"},
|
||||||
|
{"lt", "Lithuanian.txt"},
|
||||||
|
{"lt_LT", "Lithuanian.txt"},
|
||||||
|
{"lv", "English.txt"},
|
||||||
|
{"lv_LV", "English.txt"},
|
||||||
|
{"mi", "English.txt"},
|
||||||
|
{"mi_NZ", "English.txt"},
|
||||||
|
{"mk", "English.txt"},
|
||||||
|
{"mk_MK", "English.txt"},
|
||||||
|
{"mn", "English.txt"},
|
||||||
|
{"mn_MN", "English.txt"},
|
||||||
|
{"mr", "English.txt"},
|
||||||
|
{"mr_IN", "English.txt"},
|
||||||
|
{"ms", "English.txt"},
|
||||||
|
{"ms_BN", "English.txt"},
|
||||||
|
{"ms_MY", "English.txt"},
|
||||||
|
{"mt", "English.txt"},
|
||||||
|
{"mt_MT", "English.txt"},
|
||||||
|
{"nb", "English.txt"},
|
||||||
|
{"nb_NO", "English.txt"},
|
||||||
|
{"nl", "English.txt"},
|
||||||
|
{"nl_BE", "English.txt"},
|
||||||
|
{"nl_NL", "English.txt"},
|
||||||
|
{"nn_NO", "English.txt"},
|
||||||
|
{"ns", "English.txt"},
|
||||||
|
{"ns_ZA", "English.txt"},
|
||||||
|
{"pa", "English.txt"},
|
||||||
|
{"pa_IN", "English.txt"},
|
||||||
|
{"pl", "Polish.txt"},
|
||||||
|
{"pl_PL", "Polish.txt"},
|
||||||
|
{"ps", "English.txt"},
|
||||||
|
{"ps_AR", "English.txt"},
|
||||||
|
{"pt", "Portugese.txt"},
|
||||||
|
{"pt_BR", "Portugese.txt"},
|
||||||
|
{"pt_PT", "Portugese.txt"},
|
||||||
|
{"qu", "English.txt"},
|
||||||
|
{"qu_BO", "English.txt"},
|
||||||
|
{"qu_EC", "English.txt"},
|
||||||
|
{"qu_PE", "English.txt"},
|
||||||
|
{"ro", "English.txt"},
|
||||||
|
{"ro_RO", "English.txt"},
|
||||||
|
{"ru", "Russian.txt"},
|
||||||
|
{"ru_RU", "Russian.txt"},
|
||||||
|
{"sa", "English.txt"},
|
||||||
|
{"sa_IN", "English.txt"},
|
||||||
|
{"se", "English.txt"},
|
||||||
|
{"se_FI", "English.txt"},
|
||||||
|
{"se_FI", "English.txt"},
|
||||||
|
{"se_FI", "English.txt"},
|
||||||
|
{"se_NO", "English.txt"},
|
||||||
|
{"se_NO", "English.txt"},
|
||||||
|
{"se_NO", "English.txt"},
|
||||||
|
{"se_SE", "English.txt"},
|
||||||
|
{"se_SE", "English.txt"},
|
||||||
|
{"se_SE", "English.txt"},
|
||||||
|
{"sk", "English.txt"},
|
||||||
|
{"sk_SK", "English.txt"},
|
||||||
|
{"sl", "Slovenian.txt"},
|
||||||
|
{"sl_SI", "Slovenian.txt"},
|
||||||
|
{"sq", "English.txt"},
|
||||||
|
{"sq_AL", "English.txt"},
|
||||||
|
{"sr_BA", "English.txt"},
|
||||||
|
{"sr_BA", "English.txt"},
|
||||||
|
{"sr_SP", "English.txt"},
|
||||||
|
{"sr_SP", "English.txt"},
|
||||||
|
{"sv", "English.txt"},
|
||||||
|
{"sv_FI", "English.txt"},
|
||||||
|
{"sv_SE", "English.txt"},
|
||||||
|
{"sw", "English.txt"},
|
||||||
|
{"sw_KE", "English.txt"},
|
||||||
|
{"syr", "English.txt"},
|
||||||
|
{"syr_SY", "English.txt"},
|
||||||
|
{"ta", "English.txt"},
|
||||||
|
{"ta_IN", "English.txt"},
|
||||||
|
{"te", "English.txt"},
|
||||||
|
{"te_IN", "English.txt"},
|
||||||
|
{"th", "English.txt"},
|
||||||
|
{"th_TH", "English.txt"},
|
||||||
|
{"tl", "English.txt"},
|
||||||
|
{"tl_PH", "English.txt"},
|
||||||
|
{"tn", "English.txt"},
|
||||||
|
{"tn_ZA", "English.txt"},
|
||||||
|
{"tr", "Turkish.txt"},
|
||||||
|
{"tr_TR", "Turkish.txt"},
|
||||||
|
{"tt", "English.txt"},
|
||||||
|
{"tt_RU", "English.txt"},
|
||||||
|
{"ts", "English.txt"},
|
||||||
|
{"uk", "English.txt"},
|
||||||
|
{"uk_UA", "English.txt"},
|
||||||
|
{"ur", "English.txt"},
|
||||||
|
{"ur_PK", "English.txt"},
|
||||||
|
{"uz", "English.txt"},
|
||||||
|
{"uz_UZ", "English.txt"},
|
||||||
|
{"uz_UZ", "English.txt"},
|
||||||
|
{"vi", "English.txt"},
|
||||||
|
{"vi_VN", "English.txt"},
|
||||||
|
{"xh", "English.txt"},
|
||||||
|
{"xh_ZA", "English.txt"},
|
||||||
|
{"zh", "Chinese.txt"},
|
||||||
|
{"zh_CN", "Chinese.txt"},
|
||||||
|
{"zh_HK", "Chinese.txt"},
|
||||||
|
{"zh_MO", "Chinese.txt"},
|
||||||
|
{"zh_SG", "Chinese.txt"},
|
||||||
|
{"zh_TW", "Chinese_TW.txt"},
|
||||||
|
{"zu", "English.txt"},
|
||||||
|
{"zu_ZA", "English.txt"},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestResolveLicenseFile(t *testing.T) {
|
||||||
|
for _, table := range licenseTests {
|
||||||
|
os.Setenv("LANG", table.in)
|
||||||
|
f := resolveLicenseFile()
|
||||||
|
if f != table.out {
|
||||||
|
t.Errorf("resolveLicenseFile() with LANG=%v - expected %v, got %v", table.in, table.out, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
485
cmd/runmqserver/logging.go
Normal file
485
cmd/runmqserver/logging.go
Normal file
@@ -0,0 +1,485 @@
|
|||||||
|
/*
|
||||||
|
© Copyright IBM Corporation 2017, 2024
|
||||||
|
|
||||||
|
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"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ibm-messaging/mq-container/internal/command"
|
||||||
|
"github.com/ibm-messaging/mq-container/pkg/logger"
|
||||||
|
"github.com/ibm-messaging/mq-container/pkg/mqini"
|
||||||
|
)
|
||||||
|
|
||||||
|
// var debug = false
|
||||||
|
var log *logger.Logger
|
||||||
|
|
||||||
|
var collectDiagOnFail = false
|
||||||
|
|
||||||
|
func logTerminationf(format string, args ...interface{}) {
|
||||||
|
logTermination(fmt.Sprintf(format, args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func logTermination(args ...interface{}) {
|
||||||
|
msg := fmt.Sprint(args...)
|
||||||
|
// Write the message to the termination log. This is not the default place
|
||||||
|
// that Kubernetes will look for termination information.
|
||||||
|
log.Debugf("Writing termination message: %v", msg)
|
||||||
|
// #nosec G306 - its a read by owner/s group, and pose no harm.
|
||||||
|
err := os.WriteFile("/run/termination-log", []byte(msg), 0660)
|
||||||
|
if err != nil {
|
||||||
|
log.Debug(err)
|
||||||
|
}
|
||||||
|
log.Error(msg)
|
||||||
|
|
||||||
|
if collectDiagOnFail {
|
||||||
|
logDiagnostics()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getLogFormat() string {
|
||||||
|
logFormat := strings.ToLower(strings.TrimSpace(os.Getenv("MQ_LOGGING_CONSOLE_FORMAT")))
|
||||||
|
//old-style env var is used.
|
||||||
|
if logFormat == "" {
|
||||||
|
logFormat = strings.ToLower(strings.TrimSpace(os.Getenv("LOG_FORMAT")))
|
||||||
|
}
|
||||||
|
|
||||||
|
if logFormat != "" && (logFormat == "basic" || logFormat == "json") {
|
||||||
|
return logFormat
|
||||||
|
} else {
|
||||||
|
//this is the case where value is either empty string or set to something other than "basic"/"json"
|
||||||
|
logFormat = "basic"
|
||||||
|
}
|
||||||
|
|
||||||
|
return logFormat
|
||||||
|
}
|
||||||
|
|
||||||
|
// formatBasic formats a log message parsed from JSON, as "basic" text
|
||||||
|
func formatBasic(obj map[string]interface{}) string {
|
||||||
|
// Emulate the MQ "MessageDetail=Extended" option, by appending inserts to the message
|
||||||
|
// This is important for certain messages, where key details are only available in the extended message content
|
||||||
|
inserts := make([]string, 0)
|
||||||
|
for k, v := range obj {
|
||||||
|
if strings.HasPrefix(k, "ibm_commentInsert") {
|
||||||
|
inserts = append(inserts, fmt.Sprintf("%s(%v)", strings.Replace(k, "ibm_comment", "Comment", 1), obj[k]))
|
||||||
|
} else if strings.HasPrefix(k, "ibm_arithInsert") {
|
||||||
|
if v.(float64) != 0 {
|
||||||
|
inserts = append(inserts, fmt.Sprintf("%s(%v)", strings.Replace(k, "ibm_arith", "Arith", 1), obj[k]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sort.Strings(inserts)
|
||||||
|
if len(inserts) > 0 {
|
||||||
|
return fmt.Sprintf("%s %s [%v]\n", obj["ibm_datetime"], obj["message"], strings.Join(inserts, ", "))
|
||||||
|
}
|
||||||
|
// Convert time zone information from some logs (e.g. Liberty) for consistency
|
||||||
|
obj["ibm_datetime"] = strings.Replace(obj["ibm_datetime"].(string), "+0000", "Z", 1)
|
||||||
|
// Escape any new-line characters, so that we don't get multi-line messages messing up the output
|
||||||
|
obj["message"] = strings.ReplaceAll(obj["message"].(string), "\n", "\\n")
|
||||||
|
|
||||||
|
if obj["type"] != nil && (obj["type"] == "liberty_trace") {
|
||||||
|
timeStamp := obj["ibm_datetime"]
|
||||||
|
threadID := ""
|
||||||
|
srtModuleName := ""
|
||||||
|
logLevel := ""
|
||||||
|
ibmClassName := ""
|
||||||
|
srtIbmClassName := ""
|
||||||
|
ibmMethodName := ""
|
||||||
|
message := ""
|
||||||
|
|
||||||
|
if obj["loglevel"] != nil {
|
||||||
|
//threadID is captured below
|
||||||
|
if obj["ibm_threadId"] != nil {
|
||||||
|
threadID = obj["ibm_threadId"].(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
//logLevel character to be mirrored in console web server logging is decided below
|
||||||
|
logLevelTmp := obj["loglevel"].(string)
|
||||||
|
switch logLevelTmp {
|
||||||
|
case "AUDIT":
|
||||||
|
logLevel = "A"
|
||||||
|
case "INFO":
|
||||||
|
logLevel = "I"
|
||||||
|
case "EVENT":
|
||||||
|
logLevel = "1"
|
||||||
|
case "ENTRY":
|
||||||
|
logLevel = ">"
|
||||||
|
case "EXIT":
|
||||||
|
logLevel = "<"
|
||||||
|
case "FINE":
|
||||||
|
logLevel = "1"
|
||||||
|
case "FINER":
|
||||||
|
logLevel = "2"
|
||||||
|
case "FINEST":
|
||||||
|
logLevel = "3"
|
||||||
|
default:
|
||||||
|
logLevel = string(logLevelTmp[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
//This is a 13 characters string present in extracted out of module node
|
||||||
|
if obj["module"] != nil {
|
||||||
|
srtModuleNameArr := strings.Split(obj["module"].(string), ".")
|
||||||
|
arrLen := len(srtModuleNameArr)
|
||||||
|
srtModuleName = srtModuleNameArr[arrLen-1]
|
||||||
|
if len(srtModuleName) > 13 {
|
||||||
|
srtModuleName = srtModuleName[0:13]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if obj["ibm_className"] != nil {
|
||||||
|
ibmClassName = obj["ibm_className"].(string)
|
||||||
|
|
||||||
|
//A 13 character string is extracted from class name. This is required for FINE, FINER & FINEST log lines
|
||||||
|
ibmClassNameArr := strings.Split(ibmClassName, ".")
|
||||||
|
arrLen := len(ibmClassNameArr)
|
||||||
|
srtIbmClassName = ibmClassNameArr[arrLen-1]
|
||||||
|
if len(srtModuleName) > 13 {
|
||||||
|
srtIbmClassName = srtIbmClassName[0:13]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if obj["ibm_methodName"] != nil {
|
||||||
|
ibmMethodName = obj["ibm_methodName"].(string)
|
||||||
|
}
|
||||||
|
if obj["message"] != nil {
|
||||||
|
message = obj["message"].(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
//For AUDIT & INFO logging
|
||||||
|
if logLevel == "A" || logLevel == "I" {
|
||||||
|
return fmt.Sprintf("%s %s %-13s %s %s %s %s\n", timeStamp, threadID, srtModuleName, logLevel, ibmClassName, ibmMethodName, message)
|
||||||
|
}
|
||||||
|
//For EVENT logLevel
|
||||||
|
if logLevelTmp == "EVENT" {
|
||||||
|
return fmt.Sprintf("%s %s %-13s %s %s\n", timeStamp, threadID, srtModuleName, logLevel, message)
|
||||||
|
}
|
||||||
|
//For ENTRY & EXIT
|
||||||
|
if logLevel == ">" || logLevel == "<" {
|
||||||
|
return fmt.Sprintf("%s %s %-13s %s %s %s\n", timeStamp, threadID, srtModuleName, logLevel, ibmMethodName, message)
|
||||||
|
}
|
||||||
|
//For deeper log levels
|
||||||
|
if logLevelTmp == "FINE" || logLevel == "2" || logLevel == "3" {
|
||||||
|
return fmt.Sprintf("%s %s %-13s %s %s %s %s\n", timeStamp, threadID, srtIbmClassName, logLevel, ibmClassName, ibmMethodName, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s %s\n", obj["ibm_datetime"], obj["message"])
|
||||||
|
}
|
||||||
|
|
||||||
|
// mirrorSystemErrorLogs starts a goroutine to mirror the contents of the MQ system error logs
|
||||||
|
func mirrorSystemErrorLogs(ctx context.Context, wg *sync.WaitGroup, mf mirrorFunc) (chan error, error) {
|
||||||
|
// Always use the JSON log as the source
|
||||||
|
return mirrorLog(ctx, wg, "/var/mqm/errors/AMQERR01.json", false, mf, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// mirrorMQSCLogs starts a goroutine to mirror the contents of the auto-config mqsc logs
|
||||||
|
func mirrorMQSCLogs(ctx context.Context, wg *sync.WaitGroup, name string, mf mirrorFunc) (chan error, error) {
|
||||||
|
qm, err := mqini.GetQueueManager(name)
|
||||||
|
if err != nil {
|
||||||
|
log.Debug(err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
f := filepath.Join(mqini.GetErrorLogDirectory(qm), "autocfgmqsc.LOG")
|
||||||
|
return mirrorLog(ctx, wg, f, true, mf, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// mirrorQueueManagerErrorLogs starts a goroutine to mirror the contents of the MQ queue manager error logs
|
||||||
|
func mirrorQueueManagerErrorLogs(ctx context.Context, wg *sync.WaitGroup, name string, fromStart bool, mf mirrorFunc) (chan error, error) {
|
||||||
|
// Always use the JSON log as the source
|
||||||
|
qm, err := mqini.GetQueueManager(name)
|
||||||
|
if err != nil {
|
||||||
|
log.Debug(err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
f := filepath.Join(mqini.GetErrorLogDirectory(qm), "AMQERR01.json")
|
||||||
|
return mirrorLog(ctx, wg, f, fromStart, mf, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// mirrorMQSimpleAuthLogs starts a goroutine to mirror the contents of the MQ SimpleAuth authorization service's log
|
||||||
|
func mirrorMQSimpleAuthLogs(ctx context.Context, wg *sync.WaitGroup, name string, fromStart bool, mf mirrorFunc) (chan error, error) {
|
||||||
|
return mirrorLog(ctx, wg, "/var/mqm/errors/simpleauth.json", false, mf, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// mirrorWebServerLogs starts a goroutine to mirror the contents of the Liberty web server messages.log
|
||||||
|
func mirrorWebServerLogs(ctx context.Context, wg *sync.WaitGroup, name string, fromStart bool, mf mirrorFunc) (chan error, error) {
|
||||||
|
return mirrorLog(ctx, wg, "/var/mqm/web/installations/Installation1/servers/mqweb/logs/messages.log", fromStart, mf, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDebug() bool {
|
||||||
|
debug := os.Getenv("DEBUG")
|
||||||
|
if debug == "true" || debug == "1" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func configureLogger(name string) (mirrorFunc, error) {
|
||||||
|
var err error
|
||||||
|
f := getLogFormat()
|
||||||
|
d := getDebug()
|
||||||
|
switch f {
|
||||||
|
case "json":
|
||||||
|
log, err = logger.NewLogger(os.Stderr, d, true, name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return func(msg string, isQMLog bool) bool {
|
||||||
|
arrLoggingConsoleExcludeIds := strings.Split(strings.ToUpper(os.Getenv("MQ_LOGGING_CONSOLE_EXCLUDE_ID")), ",")
|
||||||
|
if isExcludedMsgIdPresent(msg, arrLoggingConsoleExcludeIds) {
|
||||||
|
//If excluded id is present do not mirror it, return back
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// Check if the message is JSON
|
||||||
|
if len(msg) > 0 && msg[0] == '{' {
|
||||||
|
obj, err := processLogMessage(msg)
|
||||||
|
if err == nil && isQMLog && filterQMLogMessage(obj) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed to unmarshall JSON in log message - %v", msg)
|
||||||
|
} else {
|
||||||
|
fmt.Println(msg)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// The log being mirrored isn't JSON. This can happen only in case of 'mqsc' logs
|
||||||
|
// Also if the logging source is from autocfgmqsc.LOG, then we have to construct the json string as per below logic
|
||||||
|
if checkLogSourceForMirroring("mqsc") && canMQSCLogBeMirroredToConsole(msg) {
|
||||||
|
logLevel := determineMQSCLogLevel(strings.TrimSpace(msg))
|
||||||
|
fmt.Printf("{\"ibm_datetime\":\"%s\",\"type\":\"mqsc_log\",\"loglevel\":\"%s\",\"message\":\"%s\"}\n",
|
||||||
|
getTimeStamp(), logLevel, strings.TrimSpace(msg))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}, nil
|
||||||
|
case "basic":
|
||||||
|
log, err = logger.NewLogger(os.Stderr, d, false, name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return func(msg string, isQMLog bool) bool {
|
||||||
|
arrLoggingConsoleExcludeIds := strings.Split(strings.ToUpper(os.Getenv("MQ_LOGGING_CONSOLE_EXCLUDE_ID")), ",")
|
||||||
|
if isExcludedMsgIdPresent(msg, arrLoggingConsoleExcludeIds) {
|
||||||
|
//If excluded id is present do not mirror it, return back
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// Check if the message is JSON
|
||||||
|
if len(msg) > 0 && msg[0] == '{' {
|
||||||
|
// Parse the JSON message, and print a simplified version
|
||||||
|
obj, err := processLogMessage(msg)
|
||||||
|
if err == nil && isQMLog && filterQMLogMessage(obj) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed to unmarshall JSON in log message - %v", err)
|
||||||
|
} else {
|
||||||
|
fmt.Print(formatBasic(obj))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// The log being mirrored isn't JSON, so just print it. This can happen only in case of mqsc logs
|
||||||
|
if checkLogSourceForMirroring("mqsc") && canMQSCLogBeMirroredToConsole(msg) {
|
||||||
|
log.Printf(strings.TrimSpace(msg))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}, nil
|
||||||
|
default:
|
||||||
|
log, err = logger.NewLogger(os.Stdout, d, false, name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("invalid value for LOG_FORMAT: %v", f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func processLogMessage(msg string) (map[string]interface{}, error) {
|
||||||
|
var obj map[string]interface{}
|
||||||
|
err := json.Unmarshal([]byte(msg), &obj)
|
||||||
|
return obj, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func filterQMLogMessage(obj map[string]interface{}) bool {
|
||||||
|
hostname, err := os.Hostname()
|
||||||
|
if os.Getenv("MQ_MULTI_INSTANCE") == "true" && err == nil && !strings.Contains(obj["host"].(string), hostname) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to check if ids provided in MQ_LOGGING_CONSOLE_EXCLUDE_ID are present in given log line or not
|
||||||
|
func isExcludedMsgIdPresent(msg string, envExcludeIds []string) bool {
|
||||||
|
for _, id := range envExcludeIds {
|
||||||
|
if id != "" && strings.Contains(msg, strings.TrimSpace(id)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func logDiagnostics() {
|
||||||
|
if getDebug() {
|
||||||
|
log.Debug("--- Start Diagnostics ---")
|
||||||
|
|
||||||
|
// show the directory ownership/permissions
|
||||||
|
// #nosec G104
|
||||||
|
out, _, _ := command.Run("ls", "-l", "/mnt/")
|
||||||
|
log.Debugf("/mnt/:\n%s", out)
|
||||||
|
// #nosec G104
|
||||||
|
out, _, _ = command.Run("ls", "-l", "/mnt/mqm")
|
||||||
|
log.Debugf("/mnt/mqm:\n%s", out)
|
||||||
|
// #nosec G104
|
||||||
|
out, _, _ = command.Run("ls", "-l", "/mnt/mqm/data")
|
||||||
|
log.Debugf("/mnt/mqm/data:\n%s", out)
|
||||||
|
// #nosec G104
|
||||||
|
out, _, _ = command.Run("ls", "-l", "/mnt/mqm-log/log")
|
||||||
|
log.Debugf("/mnt/mqm-log/log:\n%s", out)
|
||||||
|
// #nosec G104
|
||||||
|
out, _, _ = command.Run("ls", "-l", "/mnt/mqm-data/qmgrs")
|
||||||
|
log.Debugf("/mnt/mqm-data/qmgrs:\n%s", out)
|
||||||
|
// #nosec G104
|
||||||
|
out, _, _ = command.Run("ls", "-l", "/var/mqm")
|
||||||
|
log.Debugf("/var/mqm:\n%s", out)
|
||||||
|
// #nosec G104
|
||||||
|
out, _, _ = command.Run("ls", "-l", "/var/mqm/errors")
|
||||||
|
log.Debugf("/var/mqm/errors:\n%s", out)
|
||||||
|
// #nosec G104
|
||||||
|
out, _, _ = command.Run("ls", "-l", "/etc/mqm")
|
||||||
|
log.Debugf("/etc/mqm:\n%s", out)
|
||||||
|
// #nosec G104
|
||||||
|
out, _, _ = command.Run("ls", "-l", "/run")
|
||||||
|
log.Debugf("/run:\n%s", out)
|
||||||
|
|
||||||
|
// Print out summary of any FDCs
|
||||||
|
// #nosec G204
|
||||||
|
cmd := exec.Command("/opt/mqm/bin/ffstsummary")
|
||||||
|
cmd.Dir = "/var/mqm/errors"
|
||||||
|
// #nosec G104
|
||||||
|
outB, _ := cmd.CombinedOutput()
|
||||||
|
log.Debugf("ffstsummary:\n%s", string(outB))
|
||||||
|
|
||||||
|
log.Debug("--- End Diagnostics ---")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the value of MQ_LOGGING_CONSOLE_SOURCE environment variable
|
||||||
|
func getMQLogConsoleSource() string {
|
||||||
|
return strings.ToLower(strings.TrimSpace(os.Getenv("MQ_LOGGING_CONSOLE_SOURCE")))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to check if valid values are provided for environment variable MQ_LOGGING_CONSOLE_SOURCE. If not valid, main program throws a warning to console
|
||||||
|
func isLogConsoleSourceValid() bool {
|
||||||
|
mqLogSource := getMQLogConsoleSource()
|
||||||
|
retValue := false
|
||||||
|
//If nothing is set, we will mirror all, so valid
|
||||||
|
if mqLogSource == "" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
logConsoleSource := strings.Split(mqLogSource, ",")
|
||||||
|
//This will find out if the environment variable contains permitted values and is comma separated
|
||||||
|
for _, src := range logConsoleSource {
|
||||||
|
switch strings.TrimSpace(src) {
|
||||||
|
//If it is a permitted value, it is valid. Keep it as true, but dont return it. We may encounter something junk soon
|
||||||
|
case "qmgr", "web", "mqsc", "":
|
||||||
|
retValue = true
|
||||||
|
//If invalid entry arrives in-between/anywhere, just return false, there is no turning back
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return retValue
|
||||||
|
}
|
||||||
|
|
||||||
|
// To check which all logs have to be mirrored
|
||||||
|
func checkLogSourceForMirroring(source string) bool {
|
||||||
|
logsrcs := getMQLogConsoleSource()
|
||||||
|
|
||||||
|
//Nothing set, this is when we mirror both qmgr & web
|
||||||
|
if logsrcs == "" {
|
||||||
|
if source == "qmgr" || source == "web" {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Split the csv environment value so that we get an accurate comparison instead of a contains() check
|
||||||
|
logSrcArr := strings.Split(logsrcs, ",")
|
||||||
|
|
||||||
|
//Iterate through the array to decide on mirroring
|
||||||
|
for _, arr := range logSrcArr {
|
||||||
|
switch strings.TrimSpace(arr) {
|
||||||
|
case "qmgr":
|
||||||
|
//If value of source is qmgr and it exists in environment variable, mirror qmgr logs
|
||||||
|
if source == "qmgr" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
case "web":
|
||||||
|
//If value of source is web and it exists in environment variable, and mirror web logs
|
||||||
|
if source == "web" {
|
||||||
|
//If older environment variable is set make sure to print appropriate message
|
||||||
|
if os.Getenv("MQ_ENABLE_EMBEDDED_WEB_SERVER_LOG") != "" {
|
||||||
|
log.Println("Environment variable MQ_ENABLE_EMBEDDED_WEB_SERVER_LOG has now been replaced. Use MQ_LOGGING_CONSOLE_SOURCE instead.")
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
case "mqsc":
|
||||||
|
//If value of input parameter is mqsc and it exists in environment variable, mirror mqsc logs
|
||||||
|
if source == "mqsc" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// canMQSCLogBeMirroredToConsole does the following check. The autocfgmqsc.log may contain comments, empty or unformatted lines. These will be ignored
|
||||||
|
func canMQSCLogBeMirroredToConsole(message string) bool {
|
||||||
|
if len(message) > 0 && !strings.HasPrefix(strings.TrimSpace(message), ": *") && !(strings.TrimSpace(message) == ":") {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// getTimeStamp fetches current time stamp
|
||||||
|
func getTimeStamp() string {
|
||||||
|
const timestampFormat string = "2008-01-02T15:04:05.000Z07:00"
|
||||||
|
t := time.Now()
|
||||||
|
return t.Format(timestampFormat)
|
||||||
|
}
|
||||||
|
|
||||||
|
// determineMQSCLogLevel finds out log level based on if the message contains 'AMQxxxxE:' string or not.
|
||||||
|
func determineMQSCLogLevel(message string) string {
|
||||||
|
//Match the below pattern
|
||||||
|
re := regexp.MustCompile(`AMQ[0-9]+E:`)
|
||||||
|
result := re.FindStringSubmatch(message)
|
||||||
|
|
||||||
|
if len(result) > 0 {
|
||||||
|
return "ERROR"
|
||||||
|
} else {
|
||||||
|
return "INFO"
|
||||||
|
}
|
||||||
|
}
|
||||||
149
cmd/runmqserver/logging_test.go
Normal file
149
cmd/runmqserver/logging_test.go
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
/*
|
||||||
|
© Copyright IBM Corporation 2020, 2024
|
||||||
|
|
||||||
|
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 (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var formatBasicTests = []struct {
|
||||||
|
in []byte
|
||||||
|
outContains string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
[]byte("{\"ibm_datetime\":\"2020/06/24 00:00:00\",\"message\":\"Hello world\"}"),
|
||||||
|
"Hello",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]byte("{\"ibm_datetime\":\"2020/06/24 00:00:00\",\"message\":\"Hello world\", \"ibm_commentInsert1\":\"foo\"}"),
|
||||||
|
"CommentInsert1(foo)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]byte("{\"ibm_datetime\":\"2020/06/24 00:00:00\",\"message\":\"Hello world\", \"ibm_arithInsert1\":1}"),
|
||||||
|
"ArithInsert1(1)",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFormatBasic(t *testing.T) {
|
||||||
|
for i, table := range formatBasicTests {
|
||||||
|
t.Run(fmt.Sprintf("%v", i), func(t *testing.T) {
|
||||||
|
var inObj map[string]interface{}
|
||||||
|
json.Unmarshal(table.in, &inObj)
|
||||||
|
t.Logf("Unmarshalled: %+v", inObj)
|
||||||
|
out := formatBasic(inObj)
|
||||||
|
if !strings.Contains(out, table.outContains) {
|
||||||
|
t.Errorf("formatBasic() with input=%v - expected output to contain %v, got %v", string(table.in), table.outContains, out)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This test covers for functions isLogConsoleSourceValid() & checkLogSourceForMirroring()
|
||||||
|
var mqLogSourcesTests = []struct {
|
||||||
|
testNum int
|
||||||
|
logsrc string
|
||||||
|
exptValid bool
|
||||||
|
exptQmgrSrc bool
|
||||||
|
exptWebSrc bool
|
||||||
|
exptMqscSrc bool
|
||||||
|
}{
|
||||||
|
{1, "qmgr,web", true, true, true, false},
|
||||||
|
{2, "qmgr", true, true, false, false},
|
||||||
|
{3, "web,qmgr", true, true, true, false},
|
||||||
|
{4, "web", true, false, true, false},
|
||||||
|
{5, " ", true, true, true, false},
|
||||||
|
{6, "QMGR,WEB", true, true, true, false},
|
||||||
|
{7, "qmgr, ", true, true, false, false},
|
||||||
|
{8, "qmgr , web", true, true, true, false},
|
||||||
|
{9, "qmgr,dummy", false, true, false, false},
|
||||||
|
{10, "fake,dummy", false, false, false, false},
|
||||||
|
{11, "qmgr,fake,dummy", false, true, false, false},
|
||||||
|
{12, "fake,dummy,web", false, false, true, false},
|
||||||
|
{13, "true", false, false, false, false},
|
||||||
|
{14, "false", false, false, false, false},
|
||||||
|
{15, "", true, true, true, false},
|
||||||
|
{16, "mqsc", true, false, false, true},
|
||||||
|
{17, "MQSC", true, false, false, true},
|
||||||
|
{18, "qmgr,mqsc", true, true, false, true},
|
||||||
|
{19, "web,mqsc", true, false, true, true},
|
||||||
|
{20, "qmgr,web,mqsc", true, true, true, true},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoggingConsoleSourceInputs(t *testing.T) {
|
||||||
|
for _, mqlogsrctest := range mqLogSourcesTests {
|
||||||
|
err := os.Setenv("MQ_LOGGING_CONSOLE_SOURCE", mqlogsrctest.logsrc)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
isValid := isLogConsoleSourceValid()
|
||||||
|
if isValid != mqlogsrctest.exptValid {
|
||||||
|
t.Errorf("Expected return value from isLogConsoleSourceValid() is %v for MQ_LOGGING_CONSOLE_SOURCE='%v', got %v\n", mqlogsrctest.exptValid, mqlogsrctest.logsrc, isValid)
|
||||||
|
}
|
||||||
|
isLogSrcQmgr := checkLogSourceForMirroring("qmgr")
|
||||||
|
if isLogSrcQmgr != mqlogsrctest.exptQmgrSrc {
|
||||||
|
t.Errorf("Expected return value from checkLogSourceForMirroring() is %v for MQ_LOGGING_CONSOLE_SOURCE='%v', got %v\n", mqlogsrctest.exptQmgrSrc, mqlogsrctest.logsrc, isLogSrcQmgr)
|
||||||
|
}
|
||||||
|
isLogSrcWeb := checkLogSourceForMirroring("web")
|
||||||
|
if isLogSrcWeb != mqlogsrctest.exptWebSrc {
|
||||||
|
t.Errorf("Expected return value from checkLogSourceForMirroring() is %v for MQ_LOGGING_CONSOLE_SOURCE='%v', got %v\n", mqlogsrctest.exptWebSrc, mqlogsrctest.logsrc, isLogSrcWeb)
|
||||||
|
}
|
||||||
|
isLogSrcMqsc := checkLogSourceForMirroring("mqsc")
|
||||||
|
if isLogSrcMqsc != mqlogsrctest.exptMqscSrc {
|
||||||
|
t.Errorf("Expected return value from checkLogSourceForMirroring() is %v for MQ_LOGGING_CONSOLE_SOURCE='%v', got %v\n", mqlogsrctest.exptMqscSrc, mqlogsrctest.logsrc, isLogSrcMqsc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This test covers for function isExcludedMsgIdPresent()
|
||||||
|
var mqExcludeIDTests = []struct {
|
||||||
|
testNum int
|
||||||
|
exculdeIDsArr []string
|
||||||
|
expectedRetVal bool
|
||||||
|
logEntry string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
1,
|
||||||
|
[]string{"AMQ5051I", "AMQ5037I", "AMQ5975I"},
|
||||||
|
true,
|
||||||
|
"{\"ibm_messageId\":\"AMQ5051I\",\"ibm_arithInsert1\":0,\"ibm_arithInsert2\":1,\"message\":\"AMQ5051I: The queue manager task 'AUTOCONFIG' has started.\"}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
2,
|
||||||
|
[]string{"AMQ5975I", "AMQ5037I"},
|
||||||
|
false,
|
||||||
|
"{\"ibm_messageId\":\"AMQ5051I\",\"ibm_arithInsert1\":0,\"ibm_arithInsert2\":1,\"message\":\"AMQ5051I: The queue manager task 'AUTOCONFIG' has started.\"}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
3,
|
||||||
|
[]string{""},
|
||||||
|
false,
|
||||||
|
"{\"ibm_messageId\":\"AMQ5051I\",\"ibm_arithInsert1\":0,\"ibm_arithInsert2\":1,\"message\":\"AMQ5051I: The queue manager task 'AUTOCONFIG' has started.\"}",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIsExcludedMsgIDPresent(t *testing.T) {
|
||||||
|
for _, excludeIDTest := range mqExcludeIDTests {
|
||||||
|
retVal := isExcludedMsgIdPresent(excludeIDTest.logEntry, excludeIDTest.exculdeIDsArr)
|
||||||
|
if retVal != excludeIDTest.expectedRetVal {
|
||||||
|
t.Errorf("%v. Expected return value from isExcludedMsgIdPresent() is %v for MQ_LOGGING_CONSOLE_EXCLUDE_ID='%v', got %v\n",
|
||||||
|
excludeIDTest.testNum, excludeIDTest.expectedRetVal, excludeIDTest.exculdeIDsArr, retVal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
432
cmd/runmqserver/main.go
Normal file
432
cmd/runmqserver/main.go
Normal file
@@ -0,0 +1,432 @@
|
|||||||
|
/*
|
||||||
|
© Copyright IBM Corporation 2017, 2024
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// runmqserver initializes, creates and starts a queue manager, as PID 1 in a container
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"flag"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/ibm-messaging/mq-container/internal/copy"
|
||||||
|
"github.com/ibm-messaging/mq-container/internal/fips"
|
||||||
|
"github.com/ibm-messaging/mq-container/internal/ha"
|
||||||
|
"github.com/ibm-messaging/mq-container/internal/metrics"
|
||||||
|
"github.com/ibm-messaging/mq-container/internal/ready"
|
||||||
|
"github.com/ibm-messaging/mq-container/internal/simpleauth"
|
||||||
|
"github.com/ibm-messaging/mq-container/internal/tls"
|
||||||
|
"github.com/ibm-messaging/mq-container/pkg/containerruntimelogger"
|
||||||
|
"github.com/ibm-messaging/mq-container/pkg/name"
|
||||||
|
)
|
||||||
|
|
||||||
|
func doMain() error {
|
||||||
|
var initFlag = flag.Bool("i", false, "initialize volume only, then exit")
|
||||||
|
var infoFlag = flag.Bool("info", false, "Display debug info, then exit")
|
||||||
|
var noLogRuntimeFlag = flag.Bool("nologruntime", false, "used when running this program from another program, to control log output")
|
||||||
|
var devFlag = flag.Bool("dev", false, "used when running this program from runmqdevserver to control how TLS is configured")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
name, nameErr := name.GetQueueManagerName()
|
||||||
|
mf, err := configureLogger(name)
|
||||||
|
if err != nil {
|
||||||
|
logTermination(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check whether they only want debug info
|
||||||
|
if *infoFlag {
|
||||||
|
logVersionInfo()
|
||||||
|
err = containerruntimelogger.LogContainerDetails(log)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error displaying container details: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err = verifySingleProcess()
|
||||||
|
if err != nil {
|
||||||
|
// We don't do the normal termination here as it would create a termination file.
|
||||||
|
log.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if nameErr != nil {
|
||||||
|
logTermination(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = ready.Clear()
|
||||||
|
if err != nil {
|
||||||
|
logTermination(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
accepted, err := checkLicense()
|
||||||
|
if err != nil {
|
||||||
|
logTerminationf("Error checking license acceptance: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !accepted {
|
||||||
|
err = errors.New("License not accepted")
|
||||||
|
logTermination(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Printf("Using queue manager name: %v", name)
|
||||||
|
|
||||||
|
// Create a startup context to be used by the signalHandler to ensure the final reap of zombie processes only occurs after all startup processes are spawned
|
||||||
|
startupCtx, markStartupComplete := context.WithCancel(context.Background())
|
||||||
|
var startupMarkedComplete bool
|
||||||
|
// If the main thread returns before completing startup, cancel the startup context to unblock the signalHandler
|
||||||
|
defer func() {
|
||||||
|
if !startupMarkedComplete {
|
||||||
|
markStartupComplete()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
// Start signal handler
|
||||||
|
signalControl := signalHandler(name, startupCtx)
|
||||||
|
// Enable diagnostic collecting on failure
|
||||||
|
collectDiagOnFail = true
|
||||||
|
|
||||||
|
if *noLogRuntimeFlag == false {
|
||||||
|
err = containerruntimelogger.LogContainerDetails(log)
|
||||||
|
if err != nil {
|
||||||
|
logTermination(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = createVolume("/mnt/mqm/data")
|
||||||
|
if err != nil {
|
||||||
|
logTermination(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = createVolume("/mnt/mqm-log/log")
|
||||||
|
if err != nil {
|
||||||
|
logTermination(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = createVolume("/mnt/mqm-data/qmgrs")
|
||||||
|
if err != nil {
|
||||||
|
logTermination(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete contents of /run/scratch directory.
|
||||||
|
err = cleanVolume("/run/scratch")
|
||||||
|
if err != nil {
|
||||||
|
logTermination(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete contents of /run/mqm directory.
|
||||||
|
err = cleanVolume("/run/mqm")
|
||||||
|
if err != nil {
|
||||||
|
logTermination(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create ephemeral volumes
|
||||||
|
err = createVolume("/run/scratch/runmqserver")
|
||||||
|
if err != nil {
|
||||||
|
logTermination(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Queue manager i.e crtmqm command creates socket and
|
||||||
|
// others files in /run/mqm directory.
|
||||||
|
err = createVolume("/run/mqm")
|
||||||
|
if err != nil {
|
||||||
|
logTermination(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialise 15-tls.mqsc file on ephemeral volume
|
||||||
|
// #nosec G306 - its a read by owner/s group, and pose no harm.
|
||||||
|
err = os.WriteFile("/run/15-tls.mqsc", []byte(""), 0660)
|
||||||
|
if err != nil {
|
||||||
|
logTermination(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialise native-ha ini files file on ephemeral volume
|
||||||
|
nativeHAINIs := []string{
|
||||||
|
"10-native-ha.ini",
|
||||||
|
"10-native-ha-instance.ini",
|
||||||
|
"10-native-ha-keystore.ini",
|
||||||
|
}
|
||||||
|
for _, iniFile := range nativeHAINIs {
|
||||||
|
// #nosec G306 - its a read by owner/s group, and pose no harm.
|
||||||
|
err = os.WriteFile(path.Join("/run", iniFile), []byte(""), 0660)
|
||||||
|
if err != nil {
|
||||||
|
logTermination(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy default mqwebcontainer.xml file to ephemeral volume
|
||||||
|
if *devFlag && os.Getenv("MQ_DEV") == "true" {
|
||||||
|
err = copy.CopyFile("/etc/mqm/web/installations/Installation1/servers/mqweb/mqwebcontainer.xml.dev", "/run/mqwebcontainer.xml")
|
||||||
|
if err != nil {
|
||||||
|
logTermination(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = copy.CopyFile("/etc/mqm/web/installations/Installation1/servers/mqweb/mqwebcontainer.xml.default", "/run/mqwebcontainer.xml")
|
||||||
|
if err != nil {
|
||||||
|
logTermination(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy default tls.xml file to ephemeral volume
|
||||||
|
err = copy.CopyFile("/etc/mqm/web/installations/Installation1/servers/mqweb/tls.xml.default", "/run/tls.xml")
|
||||||
|
if err != nil {
|
||||||
|
logTermination(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy default jvm.options file to ephemeral volume
|
||||||
|
err = copy.CopyFile("/etc/mqm/web/installations/Installation1/servers/mqweb/configDropins/defaults/jvm.options.default", "/run/jvm.options")
|
||||||
|
if err != nil {
|
||||||
|
logTermination(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
enableTraceCrtmqdir := os.Getenv("MQ_ENABLE_TRACE_CRTMQDIR")
|
||||||
|
if enableTraceCrtmqdir == "true" || enableTraceCrtmqdir == "1" {
|
||||||
|
err = startMQTrace()
|
||||||
|
if err != nil {
|
||||||
|
logTermination(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = createDirStructure()
|
||||||
|
if err != nil {
|
||||||
|
logTermination(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if enableTraceCrtmqdir == "true" || enableTraceCrtmqdir == "1" {
|
||||||
|
err = endMQTrace()
|
||||||
|
if err != nil {
|
||||||
|
logTermination(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If init flag is set, exit now
|
||||||
|
if *initFlag {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print out versioning information
|
||||||
|
logVersionInfo()
|
||||||
|
|
||||||
|
// Determine FIPS compliance level
|
||||||
|
fips.ProcessFIPSType(log)
|
||||||
|
|
||||||
|
keyLabel, defaultCmsKeystore, defaultP12Truststore, err := tls.ConfigureDefaultTLSKeystores()
|
||||||
|
if err != nil {
|
||||||
|
logTermination(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tls.ConfigureTLS(keyLabel, defaultCmsKeystore, *devFlag, log)
|
||||||
|
if err != nil {
|
||||||
|
logTermination(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
//Validate MQ_LOG_CONSOLE_SOURCE variable
|
||||||
|
if !isLogConsoleSourceValid() {
|
||||||
|
log.Println("One or more invalid value is provided for MQ_LOGGING_CONSOLE_SOURCE. Allowed values are 'qmgr','web' and 'mqsc' in csv format")
|
||||||
|
}
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
defer func() {
|
||||||
|
log.Debug("Waiting for log mirroring to complete")
|
||||||
|
wg.Wait()
|
||||||
|
}()
|
||||||
|
ctx, cancelMirror := context.WithCancel(context.Background())
|
||||||
|
defer func() {
|
||||||
|
log.Debug("Cancel log mirroring")
|
||||||
|
cancelMirror()
|
||||||
|
}()
|
||||||
|
|
||||||
|
//For mirroring web server logs if source variable is set
|
||||||
|
if checkLogSourceForMirroring("web") {
|
||||||
|
// Always log from the end of the web server messages.log, because the log rotation should happen as soon as the web server starts
|
||||||
|
_, err = mirrorWebServerLogs(ctx, &wg, name, false, mf)
|
||||||
|
if err != nil {
|
||||||
|
logTermination(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = postInit(name, keyLabel, defaultP12Truststore)
|
||||||
|
if err != nil {
|
||||||
|
logTermination(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if os.Getenv("MQ_NATIVE_HA") == "true" {
|
||||||
|
err = ha.ConfigureNativeHA(log)
|
||||||
|
if err != nil {
|
||||||
|
logTermination(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Post FIPS initialization processing
|
||||||
|
fips.PostInit(log)
|
||||||
|
|
||||||
|
enableTraceCrtmqm := os.Getenv("MQ_ENABLE_TRACE_CRTMQM")
|
||||||
|
if enableTraceCrtmqm == "true" || enableTraceCrtmqm == "1" {
|
||||||
|
err = startMQTrace()
|
||||||
|
if err != nil {
|
||||||
|
logTermination(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
newQM, err := createQueueManager(name, *devFlag)
|
||||||
|
if err != nil {
|
||||||
|
logTermination(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if enableTraceCrtmqm == "true" || enableTraceCrtmqm == "1" {
|
||||||
|
err = endMQTrace()
|
||||||
|
if err != nil {
|
||||||
|
logTermination(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//For mirroring mq system logs and qm logs, if environment variable is set
|
||||||
|
if checkLogSourceForMirroring("qmgr") {
|
||||||
|
//Mirror MQ system logs
|
||||||
|
_, err = mirrorSystemErrorLogs(ctx, &wg, mf)
|
||||||
|
if err != nil {
|
||||||
|
logTermination(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
//Mirror queue manager logs
|
||||||
|
_, err = mirrorQueueManagerErrorLogs(ctx, &wg, name, newQM, mf)
|
||||||
|
if err != nil {
|
||||||
|
logTermination(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if *devFlag && simpleauth.IsEnabled() {
|
||||||
|
_, err = mirrorMQSimpleAuthLogs(ctx, &wg, name, newQM, mf)
|
||||||
|
if err != nil {
|
||||||
|
logTermination(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = updateCommandLevel()
|
||||||
|
if err != nil {
|
||||||
|
logTermination(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
enableTraceStrmqm := os.Getenv("MQ_ENABLE_TRACE_STRMQM")
|
||||||
|
if enableTraceStrmqm == "true" || enableTraceStrmqm == "1" {
|
||||||
|
err = startMQTrace()
|
||||||
|
if err != nil {
|
||||||
|
logTermination(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is a developer image only change
|
||||||
|
// This workaround should be removed and handled via <crtmqm -ii>, when inimerge is ready to handle stanza ordering
|
||||||
|
if *devFlag && simpleauth.IsEnabled() {
|
||||||
|
err = updateQMini(name)
|
||||||
|
if err != nil {
|
||||||
|
logTermination(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = startQueueManager(name)
|
||||||
|
if err != nil {
|
||||||
|
logTermination(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
//If the queue manager has started successfully, reflect mqsc logs when enabled
|
||||||
|
if checkLogSourceForMirroring("mqsc") {
|
||||||
|
_, err = mirrorMQSCLogs(ctx, &wg, name, mf)
|
||||||
|
if err != nil {
|
||||||
|
logTermination(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if enableTraceStrmqm == "true" || enableTraceStrmqm == "1" {
|
||||||
|
err = endMQTrace()
|
||||||
|
if err != nil {
|
||||||
|
logTermination(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enableMetrics := os.Getenv("MQ_ENABLE_METRICS")
|
||||||
|
if enableMetrics == "true" || enableMetrics == "1" {
|
||||||
|
go metrics.GatherMetrics(name, log)
|
||||||
|
} else {
|
||||||
|
log.Println("Metrics are disabled")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start reaping zombies from now on.
|
||||||
|
// Start this here, so that we don't reap any sub-processes created
|
||||||
|
// by this process (e.g. for crtmqm or strmqm)
|
||||||
|
signalControl <- startReaping
|
||||||
|
// Reap zombies now, just in case we've already got some
|
||||||
|
signalControl <- reapNow
|
||||||
|
|
||||||
|
startupMarkedComplete = true
|
||||||
|
markStartupComplete()
|
||||||
|
|
||||||
|
// Write a file to indicate that chkmqready should now work as normal
|
||||||
|
err = ready.Set()
|
||||||
|
if err != nil {
|
||||||
|
logTermination(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Wait for terminate signal
|
||||||
|
<-signalControl
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var osExit = os.Exit
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
err := doMain()
|
||||||
|
if err != nil {
|
||||||
|
osExit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
60
cmd/runmqserver/main_test.go
Normal file
60
cmd/runmqserver/main_test.go
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
© Copyright IBM Corporation 2017, 2023
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ibm-messaging/mq-container/pkg/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
var test *bool
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
test = flag.Bool("test", false, "Set to true when running tests for coverage")
|
||||||
|
log, _ = logger.NewLogger(os.Stdout, true, false, "test")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test started when the test binary is started. Only calls main.
|
||||||
|
func TestSystem(t *testing.T) {
|
||||||
|
if *test {
|
||||||
|
var oldExit = osExit
|
||||||
|
defer func() {
|
||||||
|
osExit = oldExit
|
||||||
|
}()
|
||||||
|
|
||||||
|
filename, ok := os.LookupEnv("EXIT_CODE_FILE")
|
||||||
|
if !ok {
|
||||||
|
filename = "/var/coverage/exitCode"
|
||||||
|
} else {
|
||||||
|
filename = filepath.Join("/var/coverage/", filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
osExit = func(rc int) {
|
||||||
|
// Write the exit code to a file instead
|
||||||
|
log.Printf("Writing exit code %v to file %v", strconv.Itoa(rc), filename)
|
||||||
|
err := os.WriteFile(filename, []byte(strconv.Itoa(rc)), 0644)
|
||||||
|
if err != nil {
|
||||||
|
log.Print(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
main()
|
||||||
|
}
|
||||||
|
}
|
||||||
200
cmd/runmqserver/mirror.go
Normal file
200
cmd/runmqserver/mirror.go
Normal file
@@ -0,0 +1,200 @@
|
|||||||
|
/*
|
||||||
|
© Copyright IBM Corporation 2018, 2019
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// waitForFile waits until the specified file exists
|
||||||
|
func waitForFile(ctx context.Context, path string) (os.FileInfo, error) {
|
||||||
|
var fi os.FileInfo
|
||||||
|
var err error
|
||||||
|
// Wait for file to exist
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
// Check to see if cancellation has been requested
|
||||||
|
case <-ctx.Done():
|
||||||
|
return os.Stat(path)
|
||||||
|
default:
|
||||||
|
fi, err = os.Stat(path)
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
time.Sleep(500 * time.Millisecond)
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("mirror: unable to get info on file %v", path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fi, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type mirrorFunc func(msg string, isQMLog bool) bool
|
||||||
|
|
||||||
|
// mirrorAvailableMessages prints lines from the file, until no more are available
|
||||||
|
func mirrorAvailableMessages(f *os.File, mf mirrorFunc, isQMLog bool) {
|
||||||
|
scanner := bufio.NewScanner(f)
|
||||||
|
count := 0
|
||||||
|
for scanner.Scan() {
|
||||||
|
t := scanner.Text()
|
||||||
|
if mf(t, isQMLog) {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if count > 0 {
|
||||||
|
log.Debugf("Mirrored %v log entries from %v", count, f.Name())
|
||||||
|
}
|
||||||
|
err := scanner.Err()
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Error reading file %v: %v", f.Name(), err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// mirrorLog tails the specified file, and logs each line to stdout.
|
||||||
|
// This is useful for usability, as the container console log can show
|
||||||
|
// messages from the MQ error logs.
|
||||||
|
func mirrorLog(ctx context.Context, wg *sync.WaitGroup, path string, fromStart bool, mf mirrorFunc, isQMLog bool) (chan error, error) {
|
||||||
|
errorChannel := make(chan error, 1)
|
||||||
|
var offset int64 = -1
|
||||||
|
var f *os.File
|
||||||
|
var err error
|
||||||
|
var fi os.FileInfo
|
||||||
|
// Need to check if the file exists before returning, otherwise we have a
|
||||||
|
// race to see if the new file get created before we can test for it
|
||||||
|
fi, err = os.Stat(path)
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
// File doesn't exist, so ensure we start at the beginning
|
||||||
|
offset = 0
|
||||||
|
} else {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// If the file exists, open it now, before we return. This makes sure
|
||||||
|
// the file is open before the queue manager is created or started.
|
||||||
|
// Otherwise, there would be the potential for a nearly-full file to
|
||||||
|
// rotate before the goroutine had a chance to open it.
|
||||||
|
// #nosec G304 - no harm, we open readonly and check error.
|
||||||
|
f, err = os.OpenFile(path, os.O_RDONLY, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// File already exists, so start reading at the end
|
||||||
|
offset = fi.Size()
|
||||||
|
}
|
||||||
|
// Increment wait group counter, only if the goroutine gets started
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
// Notify the wait group when this goroutine ends
|
||||||
|
defer func() {
|
||||||
|
log.Debugf("Finished monitoring %v", path)
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
if f == nil {
|
||||||
|
// File didn't exist, so need to wait for it
|
||||||
|
fi, err = waitForFile(ctx, path)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
errorChannel <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if fi == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Debugf("File exists: %v, %v", path, fi.Size())
|
||||||
|
// #nosec G304 - no harm, we open readonly and check error.
|
||||||
|
f, err = os.OpenFile(path, os.O_RDONLY, 0)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
errorChannel <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fi, err = f.Stat()
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
errorChannel <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// The file now exists. If it didn't exist before we started, offset=0
|
||||||
|
// Always start at the beginning if we've been told to go from the start
|
||||||
|
if offset != 0 && !fromStart {
|
||||||
|
log.Debugf("Seeking offset %v in file %v", offset, path)
|
||||||
|
_, err = f.Seek(offset, 0)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Unable to return to offset %v: %v", offset, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
closing := false
|
||||||
|
for {
|
||||||
|
// If there's already data there, mirror it now.
|
||||||
|
mirrorAvailableMessages(f, mf, isQMLog)
|
||||||
|
// Wait for the new log file (after rotation)
|
||||||
|
newFI, err := waitForFile(ctx, path)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
errorChannel <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !os.SameFile(fi, newFI) {
|
||||||
|
log.Debugf("Detected log rotation in file %v", path)
|
||||||
|
// WARNING: There is a possible race condition here. If *another*
|
||||||
|
// log rotation happens before we can open the new file, then we
|
||||||
|
// could skip all those messages. This could happen with a very small
|
||||||
|
// MQ error log size.
|
||||||
|
mirrorAvailableMessages(f, mf, isQMLog)
|
||||||
|
err = f.Close()
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Unable to close mirror file handle: %v", err)
|
||||||
|
}
|
||||||
|
// Re-open file
|
||||||
|
log.Debugf("Re-opening error log file %v", path)
|
||||||
|
// #nosec G304 - no harm, we open readonly and check error.
|
||||||
|
f, err = os.OpenFile(path, os.O_RDONLY, 0)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
errorChannel <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fi = newFI
|
||||||
|
// Don't seek this time, because we know it's a new file
|
||||||
|
mirrorAvailableMessages(f, mf, isQMLog)
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
log.Debugf("Context cancelled for mirroring %v", path)
|
||||||
|
if closing {
|
||||||
|
log.Debugf("Shutting down mirror for %v", path)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Set a flag, to allow one more time through the loop
|
||||||
|
closing = true
|
||||||
|
default:
|
||||||
|
time.Sleep(500 * time.Millisecond)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return errorChannel, nil
|
||||||
|
}
|
||||||
194
cmd/runmqserver/mirror_test.go
Normal file
194
cmd/runmqserver/mirror_test.go
Normal file
@@ -0,0 +1,194 @@
|
|||||||
|
/*
|
||||||
|
© Copyright IBM Corporation 2018, 2023
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMirrorLogWithoutRotation(t *testing.T) {
|
||||||
|
// Repeat the test multiple times, to help identify timing problems
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
t.Run(t.Name()+strconv.Itoa(i), func(t *testing.T) {
|
||||||
|
// Use just the sub-test name in the file name
|
||||||
|
tmp, err := os.CreateTemp("", strings.Split(t.Name(), "/")[1])
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Log(tmp.Name())
|
||||||
|
defer os.Remove(tmp.Name())
|
||||||
|
count := 0
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
_, err = mirrorLog(ctx, &wg, tmp.Name(), true, func(msg string, isQMLog bool) bool {
|
||||||
|
count++
|
||||||
|
return true
|
||||||
|
}, false)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
f, err := os.OpenFile(tmp.Name(), os.O_WRONLY, 0700)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
log.Println("Logging 3 JSON messages")
|
||||||
|
fmt.Fprintln(f, "{\"message\"=\"A\"}")
|
||||||
|
fmt.Fprintln(f, "{\"message\"=\"B\"}")
|
||||||
|
fmt.Fprintln(f, "{\"message\"=\"C\"}")
|
||||||
|
f.Close()
|
||||||
|
cancel()
|
||||||
|
wg.Wait()
|
||||||
|
if count != 3 {
|
||||||
|
t.Fatalf("Expected 3 log entries; got %v", count)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMirrorLogWithRotation(t *testing.T) {
|
||||||
|
// Repeat the test multiple times, to help identify timing problems
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
t.Run(t.Name()+strconv.Itoa(i), func(t *testing.T) {
|
||||||
|
// Use just the sub-test name in the file name
|
||||||
|
tmp, err := os.CreateTemp("", strings.Split(t.Name(), "/")[1])
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Log(tmp.Name())
|
||||||
|
defer func() {
|
||||||
|
t.Log("Removing file")
|
||||||
|
os.Remove(tmp.Name())
|
||||||
|
}()
|
||||||
|
count := 0
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
_, err = mirrorLog(ctx, &wg, tmp.Name(), true, func(msg string, isQMLog bool) bool {
|
||||||
|
count++
|
||||||
|
return true
|
||||||
|
}, false)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
f, err := os.OpenFile(tmp.Name(), os.O_WRONLY, 0700)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Log("Logging 3 JSON messages")
|
||||||
|
fmt.Fprintln(f, "{\"message\"=\"A\"}")
|
||||||
|
fmt.Fprintln(f, "{\"message\"=\"B\"}")
|
||||||
|
fmt.Fprintln(f, "{\"message\"=\"C\"}")
|
||||||
|
f.Close()
|
||||||
|
|
||||||
|
// Rotate the file, by renaming it
|
||||||
|
rotated := tmp.Name() + ".1"
|
||||||
|
os.Rename(tmp.Name(), rotated)
|
||||||
|
defer os.Remove(rotated)
|
||||||
|
// Open a new file, with the same name as before
|
||||||
|
f, err = os.OpenFile(tmp.Name(), os.O_WRONLY|os.O_CREATE, 0700)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Log("Logging 2 more JSON messages")
|
||||||
|
fmt.Fprintln(f, "{\"message\"=\"D\"}")
|
||||||
|
fmt.Fprintln(f, "{\"message\"=\"E\"}")
|
||||||
|
f.Close()
|
||||||
|
|
||||||
|
// Shut the mirroring down
|
||||||
|
cancel()
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
if count != 5 {
|
||||||
|
t.Fatalf("Expected 5 log entries; got %v", count)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testMirrorLogExistingFile(t *testing.T, newQM bool) int {
|
||||||
|
tmp, err := os.CreateTemp("", t.Name())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Log(tmp.Name())
|
||||||
|
log.Println("Logging 1 message before we start")
|
||||||
|
os.WriteFile(tmp.Name(), []byte("{\"message\"=\"A\"}\n"), 0600)
|
||||||
|
defer os.Remove(tmp.Name())
|
||||||
|
count := 0
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
_, err = mirrorLog(ctx, &wg, tmp.Name(), newQM, func(msg string, isQMLog bool) bool {
|
||||||
|
count++
|
||||||
|
return true
|
||||||
|
}, false)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
f, err := os.OpenFile(tmp.Name(), os.O_APPEND|os.O_WRONLY, 0700)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
log.Println("Logging 2 new JSON messages")
|
||||||
|
fmt.Fprintln(f, "{\"message\"=\"B\"}")
|
||||||
|
fmt.Fprintln(f, "{\"message\"=\"C\"}")
|
||||||
|
f.Close()
|
||||||
|
cancel()
|
||||||
|
wg.Wait()
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestMirrorLogExistingFile tests that we only get new log messages, if the
|
||||||
|
// log file already exists
|
||||||
|
func TestMirrorLogExistingFile(t *testing.T) {
|
||||||
|
count := testMirrorLogExistingFile(t, false)
|
||||||
|
if count != 2 {
|
||||||
|
t.Fatalf("Expected 2 log entries; got %v", count)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestMirrorLogExistingFileButNewQueueManager tests that we only get all log
|
||||||
|
// messages, even if the file exists, if we tell it we want all messages
|
||||||
|
func TestMirrorLogExistingFileButNewQueueManager(t *testing.T) {
|
||||||
|
count := testMirrorLogExistingFile(t, true)
|
||||||
|
if count != 3 {
|
||||||
|
t.Fatalf("Expected 3 log entries; got %v", count)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMirrorLogCancelWhileWaiting(t *testing.T) {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
defer func() {
|
||||||
|
cancel()
|
||||||
|
wg.Wait()
|
||||||
|
}()
|
||||||
|
_, err := mirrorLog(ctx, &wg, "fake.log", true, func(msg string, isQMLog bool) bool {
|
||||||
|
return true
|
||||||
|
}, false)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
time.Sleep(time.Second * 3)
|
||||||
|
cancel()
|
||||||
|
wg.Wait()
|
||||||
|
// No need to assert anything. If it didn't work, the code would have hung (TODO: not ideal)
|
||||||
|
}
|
||||||
59
cmd/runmqserver/post_init.go
Normal file
59
cmd/runmqserver/post_init.go
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
© Copyright IBM Corporation 2018, 2023
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/ibm-messaging/mq-container/internal/fips"
|
||||||
|
"github.com/ibm-messaging/mq-container/internal/tls"
|
||||||
|
)
|
||||||
|
|
||||||
|
// postInit is run after /var/mqm is set up
|
||||||
|
func postInit(name, keyLabel string, p12Truststore tls.KeyStoreData) error {
|
||||||
|
enableWebServer := os.Getenv("MQ_ENABLE_EMBEDDED_WEB_SERVER")
|
||||||
|
if enableWebServer == "true" || enableWebServer == "1" {
|
||||||
|
|
||||||
|
// Enable FIPS for MQ Web Server if asked for.
|
||||||
|
if fips.IsFIPSEnabled() {
|
||||||
|
err := configureFIPSWebServer(p12Truststore)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure the web server (if enabled)
|
||||||
|
webKeystore, err := configureWebServer(keyLabel, p12Truststore)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// If trust-store is empty, set reference to point to the keystore
|
||||||
|
webTruststoreRef := "MQWebTrustStore"
|
||||||
|
if len(p12Truststore.TrustedCerts) == 0 {
|
||||||
|
webTruststoreRef = "MQWebKeyStore"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the web server, in the background (if installed)
|
||||||
|
// WARNING: No error handling or health checking available for the web server
|
||||||
|
go func() {
|
||||||
|
err = startWebServer(webKeystore, p12Truststore.Password, webTruststoreRef)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error starting web server: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
64
cmd/runmqserver/process.go
Normal file
64
cmd/runmqserver/process.go
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
© Copyright IBM Corporation 2018
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/ibm-messaging/mq-container/internal/command"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Verifies that we are the main or only instance of this program
|
||||||
|
func verifySingleProcess() error {
|
||||||
|
programName, err := determineExecutable()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Failed to determine name of this program - %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify that there is only one runmqserver
|
||||||
|
_, err = verifyOnlyOne(programName)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("You cannot run more than one instance of this program")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verifies that there is only one instance running of the given program name.
|
||||||
|
func verifyOnlyOne(programName string) (int, error) {
|
||||||
|
// #nosec G104
|
||||||
|
out, _, _ := command.Run("ps", "-e", "--format", "cmd")
|
||||||
|
//if this goes wrong then assume we are the only one
|
||||||
|
numOfProg := strings.Count(out, programName)
|
||||||
|
if numOfProg != 1 {
|
||||||
|
return numOfProg, fmt.Errorf("Expected there to be only 1 instance of %s but found %d", programName, numOfProg)
|
||||||
|
}
|
||||||
|
return numOfProg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determines the name of the currently running executable.
|
||||||
|
func determineExecutable() (string, error) {
|
||||||
|
file, err := os.Executable()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, exec := filepath.Split(file)
|
||||||
|
return exec, nil
|
||||||
|
}
|
||||||
360
cmd/runmqserver/qmgr.go
Normal file
360
cmd/runmqserver/qmgr.go
Normal file
@@ -0,0 +1,360 @@
|
|||||||
|
/*
|
||||||
|
© Copyright IBM Corporation 2017, 2023
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/ibm-messaging/mq-container/internal/command"
|
||||||
|
containerruntime "github.com/ibm-messaging/mq-container/internal/containerruntime"
|
||||||
|
"github.com/ibm-messaging/mq-container/internal/mqscredact"
|
||||||
|
"github.com/ibm-messaging/mq-container/internal/mqversion"
|
||||||
|
"github.com/ibm-messaging/mq-container/internal/pathutils"
|
||||||
|
"github.com/ibm-messaging/mq-container/internal/ready"
|
||||||
|
)
|
||||||
|
|
||||||
|
// createDirStructure creates the default MQ directory structure under /var/mqm
|
||||||
|
func createDirStructure() error {
|
||||||
|
// log file diagnostics before and after crtmqdir if DEBUG=true
|
||||||
|
logDiagnostics()
|
||||||
|
out, rc, err := command.Run("/opt/mqm/bin/crtmqdir", "-f", "-a")
|
||||||
|
if err != nil {
|
||||||
|
if rc == 10 {
|
||||||
|
// Ignore warnings about 'mqwebuser.xml' being a symlink
|
||||||
|
if !(strings.Join(strings.Fields(string(out)), " ") == "The filesystem object '/mnt/mqm/data/web/installations/Installation1/servers/mqweb/mqwebuser.xml' is a symbolic link.") {
|
||||||
|
log.Printf("Warning creating directory structure: %v\n", string(out))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Printf("Error creating directory structure: the 'crtmqdir' command returned with code: %v. Reason: %v\n", rc, string(out))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.Println("Created directory structure under /var/mqm")
|
||||||
|
logDiagnostics()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// createQueueManager creates a queue manager, if it doesn't already exist.
|
||||||
|
// It returns true if one was created (or a standby was created), or false if one already existed
|
||||||
|
func createQueueManager(name string, devMode bool) (bool, error) {
|
||||||
|
log.Printf("Creating queue manager %v", name)
|
||||||
|
|
||||||
|
mounts, err := containerruntime.GetMounts()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error getting mounts for queue manager")
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
dataDir := getQueueManagerDataDir(mounts, replaceCharsInQMName(name))
|
||||||
|
|
||||||
|
// Run 'dspmqinf' to check if 'mqs.ini' configuration file exists
|
||||||
|
// If command succeeds, the queue manager (or standby queue manager) has already been created
|
||||||
|
_, _, err = command.Run("dspmqinf", name)
|
||||||
|
if err == nil {
|
||||||
|
log.Printf("Detected existing queue manager %v", name)
|
||||||
|
// Check if MQ_QMGR_LOG_FILE_PAGES matches the value set in qm.ini
|
||||||
|
lfp := os.Getenv("MQ_QMGR_LOG_FILE_PAGES")
|
||||||
|
if lfp != "" {
|
||||||
|
qmIniBytes, err := readQMIni(dataDir)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error reading qm.ini : %v", err)
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if !validateLogFilePageSetting(qmIniBytes, lfp) {
|
||||||
|
log.Println("Warning: the value of MQ_QMGR_LOG_FILE_PAGES does not match the value of 'LogFilePages' in the qm.ini. This setting cannot be altered after Queue Manager creation.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if 'qm.ini' configuration file exists for the queue manager
|
||||||
|
// TODO : handle possible race condition - use a file lock?
|
||||||
|
_, err = os.Stat(pathutils.CleanPath(dataDir, "qm.ini"))
|
||||||
|
if err != nil {
|
||||||
|
// If 'qm.ini' is not found - run 'crtmqm' to create a new queue manager
|
||||||
|
args := getCreateQueueManagerArgs(mounts, name, devMode)
|
||||||
|
out, rc, err := command.Run("crtmqm", args...)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error creating queue manager: the 'crtmqm' command returned with code: %v. Reason: %v", rc, string(out))
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// If 'qm.ini' is found - run 'addmqinf' to create a standby queue manager with existing configuration
|
||||||
|
args := getCreateStandbyQueueManagerArgs(name)
|
||||||
|
out, rc, err := command.Run("addmqinf", args...)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error creating standby queue manager: the 'addmqinf' command returned with code: %v. Reason: %v",
|
||||||
|
rc, string(out))
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
log.Println("Created standby queue manager")
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
log.Println("Created queue manager")
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// readQMIni reads the qm.ini file and returns it as a byte array
|
||||||
|
// This function is specific to comply with the nosec.
|
||||||
|
func readQMIni(dataDir string) ([]byte, error) {
|
||||||
|
qmgrDir := pathutils.CleanPath(dataDir, "qm.ini")
|
||||||
|
// #nosec G304 - qmgrDir filepath is derived from dspmqinf
|
||||||
|
iniFileBytes, err := os.ReadFile(qmgrDir)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return iniFileBytes, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// validateLogFilePageSetting validates if the specified logFilePage number is equal to the existing value in the qm.ini
|
||||||
|
func validateLogFilePageSetting(iniFileBytes []byte, logFilePages string) bool {
|
||||||
|
lfpString := "LogFilePages=" + logFilePages
|
||||||
|
qminiConfigStr := string(iniFileBytes)
|
||||||
|
return strings.Contains(qminiConfigStr, lfpString)
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateCommandLevel() error {
|
||||||
|
level, ok := os.LookupEnv("MQ_CMDLEVEL")
|
||||||
|
if ok && level != "" {
|
||||||
|
log.Printf("Setting CMDLEVEL to %v", level)
|
||||||
|
out, rc, err := command.Run("strmqm", "-e", "CMDLEVEL="+level)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error setting CMDLEVEL for queue manager: the 'strmqm' command returned with code: %v. Reason: %v",
|
||||||
|
rc, string(out))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func startQueueManager(name string) error {
|
||||||
|
log.Println("Starting queue manager")
|
||||||
|
out, rc, err := command.Run("strmqm", "-x", name)
|
||||||
|
if err != nil {
|
||||||
|
// 30=standby queue manager started, which is fine
|
||||||
|
// 94=native HA replica started, which is fine
|
||||||
|
if rc == 30 {
|
||||||
|
log.Printf("Started standby queue manager")
|
||||||
|
return nil
|
||||||
|
} else if rc == 94 {
|
||||||
|
log.Printf("Started replica queue manager")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
log.Printf("Error starting queue manager: the 'strmqm' command returned with code: %v. Reason: %v", rc, string(out))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Println("Started queue manager")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func stopQueueManager(name string) error {
|
||||||
|
log.Println("Stopping queue manager")
|
||||||
|
qmGracePeriod := os.Getenv("MQ_GRACE_PERIOD")
|
||||||
|
status, err := ready.Status(context.Background(), name)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error getting status for queue manager %v. The 'dspmq' command returned reason: %v",
|
||||||
|
name, err.Error())
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
isStandby := status.StandbyQM()
|
||||||
|
args := []string{"-w", "-r", "-tp", qmGracePeriod, name}
|
||||||
|
if os.Getenv("MQ_MULTI_INSTANCE") == "true" {
|
||||||
|
if isStandby {
|
||||||
|
args = []string{"-x", name}
|
||||||
|
} else {
|
||||||
|
args = []string{"-s", "-w", "-tp", qmGracePeriod, name}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out, rc, err := command.Run("endmqm", args...)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error stopping queue manager: the 'endmqm' command returned with code: %v. Reason: %v", rc, string(out))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if isStandby {
|
||||||
|
log.Printf("Stopped standby queue manager")
|
||||||
|
} else {
|
||||||
|
log.Println("Stopped queue manager")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func startMQTrace() error {
|
||||||
|
log.Println("Starting MQ trace")
|
||||||
|
out, rc, err := command.Run("strmqtrc")
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error starting MQ trace: the 'strmqtrc' command returned with code: %v. Reason: %v", rc, string(out))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Println("Started MQ trace")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func endMQTrace() error {
|
||||||
|
log.Println("Ending MQ Trace")
|
||||||
|
out, rc, err := command.Run("endmqtrc")
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error ending MQ trace: the 'endmqtrc' command returned with code: %v. Reason: %v", rc, string(out))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Println("Ended MQ trace")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatMQSCOutput(out string) string {
|
||||||
|
// redact sensitive information
|
||||||
|
out, _ = mqscredact.Redact(out)
|
||||||
|
|
||||||
|
// add tab characters to make it more readable as part of the log
|
||||||
|
return strings.Replace(string(out), "\n", "\n\t", -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func isStandbyQueueManager(name string) (bool, error) {
|
||||||
|
out, rc, err := command.Run("dspmq", "-n", "-m", name)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error while getting status for queue manager %v: the 'dspmq' command returned with code: %v. Reason: %v",
|
||||||
|
name, rc, string(out))
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if strings.Contains(string(out), "(RUNNING AS STANDBY)") {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getQueueManagerDataDir(mounts map[string]string, name string) string {
|
||||||
|
dataDir := pathutils.CleanPath("/var/mqm/qmgrs", name)
|
||||||
|
if _, ok := mounts["/mnt/mqm-data"]; ok {
|
||||||
|
dataDir = pathutils.CleanPath("/mnt/mqm-data/qmgrs", name)
|
||||||
|
}
|
||||||
|
return dataDir
|
||||||
|
}
|
||||||
|
|
||||||
|
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"}
|
||||||
|
|
||||||
|
if os.Getenv("MQ_NATIVE_HA") == "true" {
|
||||||
|
args = append(args, "-lr", os.Getenv("HOSTNAME"))
|
||||||
|
}
|
||||||
|
if devMode {
|
||||||
|
args = append(args, "-oa", oaVal)
|
||||||
|
}
|
||||||
|
if _, ok := mounts["/mnt/mqm-log"]; ok {
|
||||||
|
args = append(args, "-ld", "/mnt/mqm-log/log")
|
||||||
|
}
|
||||||
|
if _, ok := mounts["/mnt/mqm-data"]; ok {
|
||||||
|
args = append(args, "-md", "/mnt/mqm-data/qmgrs")
|
||||||
|
}
|
||||||
|
if os.Getenv("MQ_QMGR_LOG_FILE_PAGES") != "" {
|
||||||
|
_, err = strconv.Atoi(os.Getenv("MQ_QMGR_LOG_FILE_PAGES"))
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error processing MQ_QMGR_LOG_FILE_PAGES, the default value for LogFilePages will be used. Err: %v", err)
|
||||||
|
} else {
|
||||||
|
args = append(args, "-lf", os.Getenv("MQ_QMGR_LOG_FILE_PAGES"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
args = append(args, name)
|
||||||
|
return args
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCreateStandbyQueueManagerArgs(name string) []string {
|
||||||
|
args := []string{"-s", "QueueManager"}
|
||||||
|
args = append(args, "-v", fmt.Sprintf("Name=%v", name))
|
||||||
|
args = append(args, "-v", fmt.Sprintf("Directory=%v", name))
|
||||||
|
args = append(args, "-v", "Prefix=/var/mqm")
|
||||||
|
args = append(args, "-v", fmt.Sprintf("DataPath=/mnt/mqm-data/qmgrs/%v", name))
|
||||||
|
return args
|
||||||
|
}
|
||||||
|
|
||||||
|
// updateQMini removes the original ServicecCmponent stanza so we can add a new one
|
||||||
|
func updateQMini(qmname string) error {
|
||||||
|
|
||||||
|
val, set := os.LookupEnv("MQ_CONNAUTH_USE_HTP")
|
||||||
|
if !set {
|
||||||
|
//simpleauth mode not enabled.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
bval, err := strconv.ParseBool(strings.ToLower(val))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if bval == false {
|
||||||
|
//simpleauth mode not enabled.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("Removing existing ServiceComponent configuration")
|
||||||
|
|
||||||
|
mounts, err := containerruntime.GetMounts()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error getting mounts for queue manager")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
dataDir := getQueueManagerDataDir(mounts, replaceCharsInQMName(qmname))
|
||||||
|
qmgrDir := pathutils.CleanPath(dataDir, "qm.ini")
|
||||||
|
//read the initial version.
|
||||||
|
// #nosec G304 - qmgrDir filepath is derived from dspmqinf
|
||||||
|
iniFileBytes, err := os.ReadFile(qmgrDir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
qminiConfigStr := string(iniFileBytes)
|
||||||
|
if strings.Contains(qminiConfigStr, "ServiceComponent:") {
|
||||||
|
var re = regexp.MustCompile(`(?m)^.*ServiceComponent.*$\s^.*Service.*$\s^.*Name.*$\s^.*Module.*$\s^.*ComponentDataSize.*$`)
|
||||||
|
curFile := re.ReplaceAllString(qminiConfigStr, "")
|
||||||
|
// #nosec G304 G306 - qmgrDir filepath is derived from dspmqinf and
|
||||||
|
// its a read by owner/s group, and pose no harm.
|
||||||
|
err := os.WriteFile(qmgrDir, []byte(curFile), 0660)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// If queue manager name contains a '.', then the '.' will be replaced with '!'
|
||||||
|
// in the name of the data directory created by the queue manager. Similarly
|
||||||
|
// '/' will be replaced with '&'.
|
||||||
|
func replaceCharsInQMName(qmname string) string {
|
||||||
|
replacedName := qmname
|
||||||
|
if strings.Contains(replacedName, ".") {
|
||||||
|
replacedName = strings.ReplaceAll(replacedName, ".", "!")
|
||||||
|
}
|
||||||
|
if strings.Contains(replacedName, "/") {
|
||||||
|
replacedName = strings.ReplaceAll(replacedName, "/", "&")
|
||||||
|
}
|
||||||
|
return replacedName
|
||||||
|
}
|
||||||
122
cmd/runmqserver/qmgr_test.go
Normal file
122
cmd/runmqserver/qmgr_test.go
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
/*
|
||||||
|
© Copyright IBM Corporation 2023
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_validateLogFilePageSetting(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
iniFilePath string
|
||||||
|
isValid bool
|
||||||
|
logFilePagesValue string
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "TestLogFilePages1",
|
||||||
|
args: args{
|
||||||
|
iniFilePath: "./test-files/testvalidateLogFilePages_1.ini",
|
||||||
|
isValid: true,
|
||||||
|
logFilePagesValue: "1235",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "TestLogFilePages2",
|
||||||
|
args: args{
|
||||||
|
iniFilePath: "./test-files/testvalidateLogFilePages_2.ini",
|
||||||
|
isValid: true,
|
||||||
|
logFilePagesValue: "2224",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "TestLogFilePages3",
|
||||||
|
args: args{
|
||||||
|
iniFilePath: "./test-files/testvalidateLogFilePages_3.ini",
|
||||||
|
isValid: false,
|
||||||
|
logFilePagesValue: "1235",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "TestLogFilePages4",
|
||||||
|
args: args{
|
||||||
|
iniFilePath: "./test-files/testvalidateLogFilePages_4.ini",
|
||||||
|
isValid: false,
|
||||||
|
logFilePagesValue: "1235",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "TestLogFilePages5",
|
||||||
|
args: args{
|
||||||
|
iniFilePath: "./test-files/testvalidateLogFilePages_5.ini",
|
||||||
|
isValid: false,
|
||||||
|
logFilePagesValue: "1235",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
iniFileBytes, err := os.ReadFile(tt.args.iniFilePath)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
validate := validateLogFilePageSetting(iniFileBytes, tt.args.logFilePagesValue)
|
||||||
|
if validate != tt.args.isValid {
|
||||||
|
t.Fatalf("Expected ini file validation output to be %v got %v", tt.args.isValid, validate)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unit test for special character in queue manager names
|
||||||
|
func Test_SpecialCharInQMNameReplacements(t *testing.T) {
|
||||||
|
type qmNames struct {
|
||||||
|
qmName string
|
||||||
|
replacedQMName string
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []qmNames{
|
||||||
|
{
|
||||||
|
qmName: "QM.",
|
||||||
|
replacedQMName: "QM!",
|
||||||
|
}, {
|
||||||
|
qmName: "QM/",
|
||||||
|
replacedQMName: "QM&",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
qmName: "QM.GR.NAME",
|
||||||
|
replacedQMName: "QM!GR!NAME",
|
||||||
|
}, {
|
||||||
|
qmName: "QM/GR/NAME",
|
||||||
|
replacedQMName: "QM&GR&NAME",
|
||||||
|
}, {
|
||||||
|
qmName: "QMGRNAME",
|
||||||
|
replacedQMName: "QMGRNAME",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
replacedQMName := replaceCharsInQMName(test.qmName)
|
||||||
|
if !strings.EqualFold(replacedQMName, test.replacedQMName) {
|
||||||
|
t.Fatalf("QMName replacement failed. Expected %s but got %s\n", test.replacedQMName, replacedQMName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
124
cmd/runmqserver/signals.go
Normal file
124
cmd/runmqserver/signals.go
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
/*
|
||||||
|
© Copyright IBM Corporation 2017, 2024
|
||||||
|
|
||||||
|
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"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/ibm-messaging/mq-container/internal/metrics"
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
startReaping = iota
|
||||||
|
reapNow = iota
|
||||||
|
)
|
||||||
|
|
||||||
|
func signalHandler(qmgr string, startupCtx context.Context) chan int {
|
||||||
|
control := make(chan int)
|
||||||
|
// Use separate channels for the signals, to avoid SIGCHLD signals swamping
|
||||||
|
// the buffer, and preventing other signals.
|
||||||
|
stopSignals := make(chan os.Signal, 1)
|
||||||
|
reapSignals := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(stopSignals, syscall.SIGTERM, syscall.SIGINT)
|
||||||
|
|
||||||
|
// Pulling out as function as reused for shutdown and standard control flow
|
||||||
|
processControlSignal := func(job int) {
|
||||||
|
switch {
|
||||||
|
case job == startReaping:
|
||||||
|
// Add SIGCHLD to the list of signals we're listening to
|
||||||
|
log.Debug("Listening for SIGCHLD signals")
|
||||||
|
signal.Notify(reapSignals, syscall.SIGCHLD)
|
||||||
|
case job == reapNow:
|
||||||
|
reapZombies()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start handling signals
|
||||||
|
go func() {
|
||||||
|
shutdownCtx, shutdownComplete := context.WithCancel(context.Background())
|
||||||
|
defer func() {
|
||||||
|
shutdownComplete()
|
||||||
|
}()
|
||||||
|
stopTriggered := false
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case sig := <-stopSignals:
|
||||||
|
if stopTriggered {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
log.Printf("Signal received: %v", sig)
|
||||||
|
signal.Stop(stopSignals)
|
||||||
|
stopTriggered = true
|
||||||
|
|
||||||
|
// If a stop signal is received during the startup process continue processing control signals until the main thread marks startup as complete
|
||||||
|
// Don't close the control channel until the main thread has been allowed to finish spawning processes and marks startup as complete
|
||||||
|
// Continue to process job control signals to avoid a deadlock
|
||||||
|
done := false
|
||||||
|
for !done {
|
||||||
|
select {
|
||||||
|
// When the main thread has cancelled the startup context due to completion or an error stop processing control signals
|
||||||
|
case <-startupCtx.Done():
|
||||||
|
done = true
|
||||||
|
// Keep processing control signals until the main thread has finished its startup
|
||||||
|
case job := <-control:
|
||||||
|
processControlSignal(job)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
metrics.StopMetricsGathering(log)
|
||||||
|
|
||||||
|
// Shutdown queue manager in separate goroutine to allow reaping to continue in parallel
|
||||||
|
go func() {
|
||||||
|
_ = stopQueueManager(qmgr)
|
||||||
|
shutdownComplete()
|
||||||
|
}()
|
||||||
|
case <-shutdownCtx.Done():
|
||||||
|
signal.Stop(reapSignals)
|
||||||
|
|
||||||
|
// One final reap
|
||||||
|
// This occurs after all startup processes have been spawned
|
||||||
|
reapZombies()
|
||||||
|
|
||||||
|
close(control)
|
||||||
|
// End the goroutine
|
||||||
|
return
|
||||||
|
case <-reapSignals:
|
||||||
|
log.Debug("Received SIGCHLD signal")
|
||||||
|
reapZombies()
|
||||||
|
case job := <-control:
|
||||||
|
processControlSignal(job)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return control
|
||||||
|
}
|
||||||
|
|
||||||
|
// reapZombies reaps any zombie (terminated) processes now.
|
||||||
|
// This function should be called before exiting.
|
||||||
|
func reapZombies() {
|
||||||
|
for {
|
||||||
|
var ws unix.WaitStatus
|
||||||
|
pid, err := unix.Wait4(-1, &ws, unix.WNOHANG, nil)
|
||||||
|
// If err or pid indicate "no child processes"
|
||||||
|
if pid == 0 || err == unix.ECHILD {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Debugf("Reaped PID %v", pid)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
ExitPath:
|
||||||
|
ExitsDefaultPath=/mnt/mqm/data/exits
|
||||||
|
ExitsDefaultPath64=/mnt/mqm/data/exits64
|
||||||
|
Log:
|
||||||
|
LogPrimaryFiles=3
|
||||||
|
LogSecondaryFiles=2
|
||||||
|
LogFilePages=1235
|
||||||
|
LogBufferPages=0
|
||||||
|
LogWriteIntegrity=TripleWrite
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
ExitPath:
|
||||||
|
ExitsDefaultPath=/mnt/mqm/data/exits
|
||||||
|
ExitsDefaultPath64=/mnt/mqm/data/exits64
|
||||||
|
Log:
|
||||||
|
LogPrimaryFiles=3
|
||||||
|
LogSecondaryFiles=2
|
||||||
|
LogFilePages=2224
|
||||||
|
LogBufferPages=0
|
||||||
|
LogWriteIntegrity=TripleWrite
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
ExitPath:
|
||||||
|
ExitsDefaultPath=/mnt/mqm/data/exits
|
||||||
|
ExitsDefaultPath64=/mnt/mqm/data/exits64
|
||||||
|
Log:
|
||||||
|
LogPrimaryFiles=3
|
||||||
|
LogSecondaryFiles=2
|
||||||
|
LogFilePages=6002
|
||||||
|
LogBufferPages=0
|
||||||
|
LogWriteIntegrity=TripleWrite
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
ExitPath:
|
||||||
|
ExitsDefaultPath=/mnt/mqm/data/exits
|
||||||
|
ExitsDefaultPath64=/mnt/mqm/data/exits64
|
||||||
|
Log:
|
||||||
|
LogPrimaryFiles=3
|
||||||
|
LogSecondaryFiles=2
|
||||||
|
LogBufferPages=0
|
||||||
|
LogWriteIntegrity=TripleWrite
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
ExitPath:
|
||||||
|
ExitsDefaultPath=/mnt/mqm/data/exits
|
||||||
|
ExitsDefaultPath64=/mnt/mqm/data/exits64
|
||||||
|
Log:
|
||||||
|
LogPrimaryFiles=3
|
||||||
|
LogSecondaryFiles=2
|
||||||
|
LogBufferPages=1235
|
||||||
|
LogWriteIntegrity=TripleWrite
|
||||||
79
cmd/runmqserver/version.go
Normal file
79
cmd/runmqserver/version.go
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
/*
|
||||||
|
© Copyright IBM Corporation 2018
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/ibm-messaging/mq-container/internal/command"
|
||||||
|
"github.com/ibm-messaging/mq-container/internal/mqversion"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ImageCreated is the date the image was built
|
||||||
|
ImageCreated = "Not specified"
|
||||||
|
// ImageRevision is the source control revision identifier
|
||||||
|
ImageRevision = "Not specified"
|
||||||
|
// ImageSource is the URL to get source code for building the image
|
||||||
|
ImageSource = "Not specified"
|
||||||
|
// ImageTag is the tag of the image
|
||||||
|
ImageTag = "Not specified"
|
||||||
|
)
|
||||||
|
|
||||||
|
func logDateStamp() {
|
||||||
|
log.Printf("Image created: %v", ImageCreated)
|
||||||
|
}
|
||||||
|
|
||||||
|
func logGitRepo() {
|
||||||
|
// log.Printf("Image revision: %v", ImageRevision)
|
||||||
|
}
|
||||||
|
|
||||||
|
func logGitCommit() {
|
||||||
|
// log.Printf("Image source: %v", ImageSource)
|
||||||
|
}
|
||||||
|
|
||||||
|
func logImageTag() {
|
||||||
|
log.Printf("Image tag: %v", ImageTag)
|
||||||
|
}
|
||||||
|
|
||||||
|
func logMQVersion() {
|
||||||
|
mqVersion, err := mqversion.Get()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error Getting MQ version: %v", strings.TrimSuffix(string(mqVersion), "\n"))
|
||||||
|
}
|
||||||
|
|
||||||
|
mqBuild, _, err := command.Run("dspmqver", "-b", "-f", "4")
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error Getting MQ build: %v", strings.TrimSuffix(string(mqBuild), "\n"))
|
||||||
|
}
|
||||||
|
mqLicense, _, err := command.Run("dspmqver", "-b", "-f", "8192")
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error Getting MQ license: %v", strings.TrimSuffix(string(mqLicense), "\n"))
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("MQ version: %v", strings.TrimSuffix(mqVersion, "\n"))
|
||||||
|
log.Printf("MQ level: %v", strings.TrimSuffix(mqBuild, "\n"))
|
||||||
|
log.Printf("MQ license: %v", strings.TrimSuffix(mqLicense, "\n"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func logVersionInfo() {
|
||||||
|
logDateStamp()
|
||||||
|
logGitRepo()
|
||||||
|
logGitCommit()
|
||||||
|
logImageTag()
|
||||||
|
logMQVersion()
|
||||||
|
}
|
||||||
172
cmd/runmqserver/webserver.go
Normal file
172
cmd/runmqserver/webserver.go
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
/*
|
||||||
|
© Copyright IBM Corporation 2018, 2024
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/ibm-messaging/mq-container/internal/copy"
|
||||||
|
"github.com/ibm-messaging/mq-container/internal/mqtemplate"
|
||||||
|
"github.com/ibm-messaging/mq-container/internal/tls"
|
||||||
|
)
|
||||||
|
|
||||||
|
func startWebServer(webKeystore, webkeystorePW, webTruststoreRef string) error {
|
||||||
|
_, err := os.Stat("/opt/mqm/bin/strmqweb")
|
||||||
|
if err != nil && os.IsNotExist(err) {
|
||||||
|
log.Debug("Skipping web server, because it's not installed")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
log.Println("Starting web server")
|
||||||
|
// #nosec G204 - command is fixed, no injection vector
|
||||||
|
cmd := exec.Command("strmqweb")
|
||||||
|
|
||||||
|
// Pass all the environment to MQ Web Server JVM
|
||||||
|
cmd.Env = os.Environ()
|
||||||
|
|
||||||
|
// TLS enabled
|
||||||
|
if webKeystore != "" {
|
||||||
|
cmd.Env = append(cmd.Env, "AMQ_WEBKEYSTORE="+webKeystore)
|
||||||
|
cmd.Env = append(cmd.Env, "AMQ_WEBKEYSTOREPW="+webkeystorePW)
|
||||||
|
cmd.Env = append(cmd.Env, "AMQ_WEBTRUSTSTOREREF="+webTruststoreRef)
|
||||||
|
}
|
||||||
|
out, err := cmd.CombinedOutput()
|
||||||
|
rc := cmd.ProcessState.ExitCode()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error %v starting web server: %v", rc, string(out))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Println("Started web server")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func configureWebServer(keyLabel string, p12Truststore tls.KeyStoreData) (string, error) {
|
||||||
|
|
||||||
|
webKeystore := ""
|
||||||
|
|
||||||
|
// Copy server.xml file to ensure that we have the latest expected contents - this file is only populated on QM creation
|
||||||
|
err := copy.CopyFile("/opt/mqm/samp/web/server.xml", "/var/mqm/web/installations/Installation1/servers/mqweb/server.xml")
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure TLS for the Web Console
|
||||||
|
err = tls.ConfigureWebTLS(keyLabel, log, p12Truststore.Password)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure the Web Keystore
|
||||||
|
if keyLabel != "" || os.Getenv("MQ_GENERATE_CERTIFICATE_HOSTNAME") != "" {
|
||||||
|
webKeystore, err = tls.ConfigureWebKeystore(p12Truststore, keyLabel)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = os.Stat("/opt/mqm/bin/strmqweb")
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
const webConfigDir string = "/etc/mqm/web"
|
||||||
|
_, err = os.Stat(webConfigDir)
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
const prefix string = "/etc/mqm/web"
|
||||||
|
err = filepath.Walk(prefix, func(from string, info os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
to := fmt.Sprintf("/var/mqm/web%v", from[len(prefix):])
|
||||||
|
exists := true
|
||||||
|
_, err = os.Stat(to)
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
exists = false
|
||||||
|
} else {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if info.IsDir() {
|
||||||
|
if !exists {
|
||||||
|
// #nosec G301 - write group permissions are required
|
||||||
|
err := os.MkdirAll(to, 0770)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if exists {
|
||||||
|
err := os.Remove(to)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use a symlink for file 'mqwebuser.xml', so that if it contains a secret, it doesn't get persisted to a volume
|
||||||
|
if strings.HasSuffix(from, "/mqwebuser.xml") {
|
||||||
|
err = os.Symlink(from, to)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
err := copy.CopyFile(from, to)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
return webKeystore, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure FIPS mode for MQ Web Server
|
||||||
|
func configureFIPSWebServer(p12TrustStore tls.KeyStoreData) error {
|
||||||
|
|
||||||
|
// Need to update jvm.options file of MQ Web Server. We don't update the jvm.options file
|
||||||
|
// in /etc/mqm/web/installations/Installation1/servers/mqweb directory. Instead we update
|
||||||
|
// the one in /etc/mqm/web/installations/Installation1/servers/mqweb/configDropins/defaults.
|
||||||
|
// During runtime MQ Web Server merges the data from two files.
|
||||||
|
const jvmOptsLink string = "/run/jvm.options"
|
||||||
|
const jvmOptsTemplate string = "/etc/mqm/web/installations/Installation1/servers/mqweb/configDropins/defaults/jvm.options.tpl"
|
||||||
|
|
||||||
|
// Update the jvm.options file using the data from template file. Tell the MQ Web Server
|
||||||
|
// use a FIPS provider by setting "-Dcom.ibm.jsse2.usefipsprovider=true" and then tell it
|
||||||
|
// use a specific FIPS provider by setting "Dcom.ibm.jsse2.usefipsProviderName=IBMJCEPlusFIPS".
|
||||||
|
err := mqtemplate.ProcessTemplateFile(jvmOptsTemplate, jvmOptsLink, map[string]string{
|
||||||
|
"FipsProvider": "true",
|
||||||
|
"FipsProviderName": "IBMJCEPlusFIPS",
|
||||||
|
}, log)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
6
config.env
Normal file
6
config.env
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
###########################################################################################################################################################
|
||||||
|
|
||||||
|
# MQ_VERSION is the fully qualified MQ version number to build
|
||||||
|
MQ_VERSION ?= 9.4.1.0
|
||||||
|
|
||||||
|
###########################################################################################################################################################
|
||||||
17
docker-builds/skopeo/Dockerfile
Normal file
17
docker-builds/skopeo/Dockerfile
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# © 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.
|
||||||
|
|
||||||
|
FROM fedora:32
|
||||||
|
RUN yum install skopeo -y -qq
|
||||||
|
ENTRYPOINT [ "skopeo" ]
|
||||||
57
docs/building.md
Executable file
57
docs/building.md
Executable file
@@ -0,0 +1,57 @@
|
|||||||
|
# Building a container image
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
You need to have the following tools installed:
|
||||||
|
|
||||||
|
* [Docker](https://www.docker.com/) 20.10 or later, or [Podman](https://podman.io) 4.4 or later.
|
||||||
|
* [GNU make](https://www.gnu.org/software/make/)
|
||||||
|
|
||||||
|
## Building Images
|
||||||
|
|
||||||
|
To build an IBM MQ image, navigate to the appropriate section:
|
||||||
|
|
||||||
|
- [Building a production image](#building-a-production-image)
|
||||||
|
- [Building a developer image](#building-a-developer-image)
|
||||||
|
|
||||||
|
## Building a production image
|
||||||
|
|
||||||
|
### MQ Continuous Delivery (CD)
|
||||||
|
|
||||||
|
The procedure below is for building the 9.4.1 release, on `amd64`, `ppc64le` and `s390x` architectures.
|
||||||
|
|
||||||
|
1. Create a `downloads` directory in the root of this repository
|
||||||
|
2. Identify the correct eImage part number for your architecture from https://www.ibm.com/support/pages/downloading-ibm-mq-94 and download
|
||||||
|
3. Ensure the `tar.gz` file is in the `downloads` directory
|
||||||
|
4. Run `make build-advancedserver`
|
||||||
|
|
||||||
|
If you have an MQ archive file with a different file name, you can specify a particular file (which must be in the `downloads` directory). You should also specify the MQ version, so that the resulting image is tagged correctly, for example:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
MQ_ARCHIVE=mq-1.2.3.4.tar.gz MQ_VERSION=1.2.3.4 make build-advancedserver
|
||||||
|
```
|
||||||
|
|
||||||
|
### MQ LTS
|
||||||
|
|
||||||
|
The procedure below is for building the 9.4.0 release, on `amd64`, `ppc64le` and `s390x` architectures.
|
||||||
|
|
||||||
|
1. Create a `downloads` directory in the root of this repository
|
||||||
|
2. Identify the correct eImage part number for your architecture from https://www.ibm.com/support/pages/downloading-ibm-mq-94 and download
|
||||||
|
3. Ensure the `tar.gz` file is in the `downloads` directory
|
||||||
|
4. Run `make build-advancedserver`
|
||||||
|
|
||||||
|
If you have an MQ archive file with a different file name, you can specify a particular file (which must be in the `downloads` directory). You should also specify the MQ version, so that the resulting image is tagged correctly, for example:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
MQ_ARCHIVE=mq-1.2.3.4.tar.gz MQ_VERSION=1.2.3.4 make build-advancedserver
|
||||||
|
```
|
||||||
|
|
||||||
|
## Building a developer image
|
||||||
|
|
||||||
|
Run `make build-devserver`, which will download the latest version of MQ Advanced for Developers. This is available on the `amd64` and `arm64` (Apple Silicon) architectures.
|
||||||
|
|
||||||
|
You can use the environment variable `MQ_ARCHIVE_DEV` to specify an alternative local file to install from (which must be in the `downloads` directory).
|
||||||
|
|
||||||
|
## Installed components
|
||||||
|
|
||||||
|
This image includes the core MQ server, Java, language packs, GSKit, and web server. This is configured in the `mq-redux` build stage in `Dockerfile-server`.
|
||||||
75
docs/developer-config.md
Normal file
75
docs/developer-config.md
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
# Default developer configuration
|
||||||
|
|
||||||
|
If you build this image with MQ Advanced for Developers, then an optional set of configuration can be applied automatically. This configures your Queue Manager with a set of default objects that you can use to quickly get started developing with IBM MQ. If you do not want the default objects to be created you can set the `MQ_DEV` environment variable to `false`.
|
||||||
|
|
||||||
|
## Using Secrets to set passwords for app & admin users
|
||||||
|
|
||||||
|
Secrets must be used to set the passwords for `admin` and `app` user. For setting password for user `admin`, `mqAdminPassword` secret must be created and for user `app`, `mqAppPassword` secret must be created.
|
||||||
|
|
||||||
|
### Example usage with podman:
|
||||||
|
|
||||||
|
Create podman secrets with secret names as “mqAdminPassword” & "mqAppPassword":
|
||||||
|
|
||||||
|
- `printf "passw0rd" | podman secret create mqAdminPassword -`
|
||||||
|
- `printf "passw0rd" | podman secret create mqAppPassword -`
|
||||||
|
|
||||||
|
Run container referencing mounted secrets:
|
||||||
|
- `podman run --secret mqAdminPassword,type=mount,mode=0777 --secret mqAppPassword,type=mount,mode=0777 --env LICENSE=accept --env MQ_QMGR_NAME=QM1 --publish 1414:1414 --publish 9443:9443 --detach --name QM1 icr.io/ibm-messaging/mq:latest`
|
||||||
|
|
||||||
|
### Example usage with docker:
|
||||||
|
|
||||||
|
Docker secrets are only available via Docker Swarm services, hence to create a secret using docker, Docker Swarm must be used.
|
||||||
|
|
||||||
|
Create docker secrets with secret names as “mqAdminPassword” & "mqAppPassword":
|
||||||
|
|
||||||
|
- `printf "passw0rd" | docker secret create mqAdminPassword –`
|
||||||
|
- `printf "passw0rd" | docker secret create mqAppPassword –`
|
||||||
|
|
||||||
|
Run container referencing mounted secret:
|
||||||
|
- `docker service create --secret mqAdminPassword --secret mqAppPassword --env LICENSE=accept --env MQ_QMGR_NAME=QM8 --publish 1414:1414 --publish 9443:9443 --detach --name QM8 icr.io/ibm-messaging/mq`
|
||||||
|
|
||||||
|
## Environment variables
|
||||||
|
|
||||||
|
From IBM MQ v9.4.0.0, environment variables `MQ_ADMIN_PASSWORD` and `MQ_APP_PASSWORD` are deprecated. Secrets as detailed in the previous section must be used to set the passwords for `admin` and `app` user.
|
||||||
|
|
||||||
|
The MQ Developer Defaults supports some customization options, these are all controlled using environment variables:
|
||||||
|
|
||||||
|
* **MQ_DEV** - Set this to `false` to stop the default objects being created.
|
||||||
|
* **MQ_ADMIN_PASSWORD** - Specify the password of the `admin` user. Must be at least 8 characters long.
|
||||||
|
* **MQ_APP_PASSWORD** - Specify the password of the `app` user. If set, this will cause the `DEV.APP.SVRCONN` channel to become secured and only allow connections that supply a valid userid and password. Must be at least 8 characters long.
|
||||||
|
|
||||||
|
|
||||||
|
## Details of the default configuration
|
||||||
|
|
||||||
|
The following users are created:
|
||||||
|
|
||||||
|
* User **admin** for administration. This user is created only if the password is set. Secrets must be used to set the password.
|
||||||
|
* User **app** for messaging (in a group called `mqclient`). This user is created only if the password is set. Secrets must be used to set the password.
|
||||||
|
|
||||||
|
Users in `mqclient` group have been given access connect to all queues and topics starting with `DEV.**` and have `put`, `get`, `pub`, `sub`, `browse` and `inq` permissions.
|
||||||
|
|
||||||
|
The following queues and topics are created:
|
||||||
|
|
||||||
|
* DEV.QUEUE.1
|
||||||
|
* DEV.QUEUE.2
|
||||||
|
* DEV.QUEUE.3
|
||||||
|
* DEV.DEAD.LETTER.QUEUE - configured as the Queue Manager's Dead Letter Queue.
|
||||||
|
* DEV.BASE.TOPIC - uses a topic string of `dev/`.
|
||||||
|
|
||||||
|
Two channels are created, one for administration, the other for normal messaging:
|
||||||
|
|
||||||
|
* DEV.ADMIN.SVRCONN - configured to only allow the admin user to connect into it. The `admin` user can be used with the password configured via secret.
|
||||||
|
* DEV.APP.SVRCONN - does not allow administrative users to connect. Only the `app` user can connect. The password would be as configured by the secret.
|
||||||
|
|
||||||
|
## Web Console
|
||||||
|
|
||||||
|
By default the MQ Advanced for Developers image will start the IBM MQ Web Console that allows you to administer your Queue Manager running on your container. When the web console has been started, you can access it by opening a web browser and navigating to `https://<Container IP>:9443/ibmmq/console`. Where `<Container IP>` is replaced by the IP address of your running container.
|
||||||
|
|
||||||
|
When you navigate to this page you may be presented with a security exception warning. This happens because, by default, the web console creates a self-signed certificate to use for the HTTPS operations. This certificate is not trusted by your browser and has an incorrect distinguished name.
|
||||||
|
|
||||||
|
If you choose to accept the security warning, you will be presented with the login menu for the IBM MQ Web Console. The login for the console is:
|
||||||
|
|
||||||
|
* **User:** admin
|
||||||
|
* **Password:** The password for the `admin` user must be specified using a secret, as described above.
|
||||||
|
|
||||||
|
If you do not wish the web console to run, you can disable it by setting the environment variable `MQ_ENABLE_EMBEDDED_WEB_SERVER` to `false`.
|
||||||
52
docs/internals.md
Normal file
52
docs/internals.md
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
# Internals
|
||||||
|
|
||||||
|
This page documents internal code details and design decisions.
|
||||||
|
|
||||||
|
The resulting Docker image contains the following:
|
||||||
|
|
||||||
|
* Base linux distribution - this provides standard Linux libraries (such as "glibc") and utilities (such as "ls" and "grep") required by MQ
|
||||||
|
* MQ installation (under `/opt/mqm`)
|
||||||
|
* Three additional programs, to enable running in a containerized environment:
|
||||||
|
- `runmqserver` - The main process, which creates and runs a queue manager
|
||||||
|
- `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.
|
||||||
|
- `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
|
||||||
|
The `runmqserver` command has the following responsibilities:
|
||||||
|
|
||||||
|
* Checks license acceptance
|
||||||
|
* Sets up `/var/mqm`
|
||||||
|
- MQ data directory needs to be set up at container creation time. This is done using the `crtmqdir` utility, which was introduced in MQ V9.0.3
|
||||||
|
- It assumes that a storage volume for data is mounted under `/mnt/mqm`. It creates a sub-directory for the MQ data, so `/var/mqm` is a symlink which resolves to `/mnt/mqm/data`. The reason for this is that it's not always possible to change the ownership of an NFS mount point directly (`/var/mqm` needs to be owned by "mqm"), but you can change the ownership of a sub-directory.
|
||||||
|
* Acts like a daemon
|
||||||
|
- Handles UNIX signals, like SIGTERM
|
||||||
|
- Works as PID 1, so is responsible for [reaping zombie processes](https://blog.phusion.nl/2015/01/20/docker-and-the-pid-1-zombie-reaping-problem/)
|
||||||
|
* Creating and starting a queue manager
|
||||||
|
* Configuring the queue manager, by running any MQSC scripts found under `/etc/mqm`
|
||||||
|
* Starts the MQ web server (if enabled)
|
||||||
|
* Starting Prometheus metrics generation for the queue manager (if enabled)
|
||||||
|
* Indicates to the `chkmqready` command that configuration is complete, and that normal readiness checking can happen. This is done by writing a file into `/run/runmqserver`
|
||||||
|
|
||||||
|
In addition, for MQ Advanced for Developers only, the web server is started.
|
||||||
|
|
||||||
|
## runmqdevserver
|
||||||
|
The `runmqdevserver` command is added to the MQ Advanced for Developers image only. It does the following, before invoking `runmqserver`:
|
||||||
|
|
||||||
|
1. Sets passwords based on supplied environment variables
|
||||||
|
2. Generates MQSC files to put in `/etc/mqm`, based on a template, which is updated with values based on supplied environment variables.
|
||||||
|
3. If requested, it creates TLS key stores under `/run/runmqserver`, and configures MQ and the web server to use them
|
||||||
|
|
||||||
|
## Prometheus metrics
|
||||||
|
[Prometheus](https://prometheus.io) metrics are generated for the queue manager as follows:
|
||||||
|
|
||||||
|
1. A connection is established with the queue manager
|
||||||
|
2. Metrics are discovered by subscribing to topics that provide meta-data on metric classes, types and elements
|
||||||
|
3. Subscriptions are then created for each topic that provides this metric data
|
||||||
|
4. Metrics are initialised using Prometheus names mapped from their element descriptions
|
||||||
|
5. The metrics are then registered with the Prometheus registry as Prometheus Gauges
|
||||||
|
6. Publications are processed on a periodic basis to retrieve the metric data
|
||||||
|
7. An HTTP server is setup to listen for requests from Prometheus on `/metrics` port `9157`
|
||||||
|
8. Prometheus requests are handled by updating the Prometheus Gauges with the latest metric data
|
||||||
|
9. These updated Prometheus Gauges are then collected by the Prometheus registry
|
||||||
28
docs/pluggable-connauth.md
Normal file
28
docs/pluggable-connauth.md
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
|
||||||
|
### Queue Manager Connection Authentication using secrets
|
||||||
|
|
||||||
|
Prior to IBM MQ v9.4.0.0, passwords could be supplied through MQ_ADMIN_PASSWORD and MQ_APP_PASSWORD environment variables. From IBM MQ v9.4.0.0, supplying passwords through environment variables is deprecated and not recommended. IBM MQ v9.4.0.0 provides a new authentication mode to allow developers using mq-container developer image to authenticate users. In this new authentication mode passwords for `app` and `admin` users are supplied through secrets securely mounted into the file system. Secret with name `mqAppPassword` and `mqAdminPassword` can now be used to supply password for users `app` and `admin`. This is in addition to the existing methods of users authentication, described in [User authentication and authorization for IBM MQ in containers] (https://www.ibm.com/docs/en/ibm-mq/latest?topic=containers-user-authentication-authorization-mq-in)
|
||||||
|
|
||||||
|
**Please note:**
|
||||||
|
1. This new feature is enabled only when environment variable `MQ_CONNAUTH_USE_HTP=true` is set while starting the MQ Container.
|
||||||
|
2. When enabled, the `AuthType` value of the ConnectionAuthentication (`CONNAUTH`) is ignored and secrets are used. However,
|
||||||
|
the MQ authority records created using (`setmqaut` or `AUTHREC`) will be in effect while using the secrets.
|
||||||
|
3. Channel Authentication records (`CHLAUTH`) will be in effect while using the secrets.
|
||||||
|
4. This is developer only feature and not recommended for use in Production.
|
||||||
|
|
||||||
|
### Using Secrets
|
||||||
|
|
||||||
|
1. `mqAppPassword` and `mqAdminPassword` secrets passed to the container are mounted under /run/secrets directory. These secrets are used for authentication of `app` or `admin` users. It must be noted that `app` and `admin` user do not have any default password.
|
||||||
|
2. The `app` user is authorized to access `DEV.*` objects of the queue manager.
|
||||||
|
|
||||||
|
#### Next Steps:
|
||||||
|
|
||||||
|
Use an administrative tool or your application to connect to queue manager using the passwords that are set as secrets for user `app` and `admin`.
|
||||||
|
|
||||||
|
**Please note**: When an authentication request is made with a userid other than `app` or `admin`, then the authentication process is delegated to queue manager to handle. This will then use `IDPWOS` or `LDAP` modes for further processing.
|
||||||
|
|
||||||
|
#### Troubleshooting
|
||||||
|
|
||||||
|
A log file named `mqsimpleauth.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 will be wiped when the queue manager is next started.
|
||||||
20
docs/security.md
Normal file
20
docs/security.md
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
# Security
|
||||||
|
|
||||||
|
## Container runtime
|
||||||
|
|
||||||
|
### User
|
||||||
|
|
||||||
|
The MQ server image is run using with UID 1001, though this can be any UID, with a fixed GID of 0 (root).
|
||||||
|
|
||||||
|
### Capabilities
|
||||||
|
|
||||||
|
The MQ Advanced image requires no Linux capabilities, so you can drop any capabilities which are added by default. For example, in Docker you could do the following:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
docker run \
|
||||||
|
--cap-drop=ALL \
|
||||||
|
--env LICENSE=accept \
|
||||||
|
--env MQ_QMGR_NAME=QM1 \
|
||||||
|
--detach \
|
||||||
|
ibm-mqadvanced-server:9.4.1.0-amd64
|
||||||
|
```
|
||||||
56
docs/testing.md
Normal file
56
docs/testing.md
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
# Testing
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
You need to ensure you have the following tools installed:
|
||||||
|
* [Docker](https://www.docker.com/) 19.03 or higher (API version 1.40)
|
||||||
|
* [GNU make](https://www.gnu.org/software/make/)
|
||||||
|
* [Go](https://golang.org/) - only needed for running the tests
|
||||||
|
|
||||||
|
## Running the tests
|
||||||
|
There are two main sets of tests:
|
||||||
|
|
||||||
|
1. Unit tests, which are run during a build
|
||||||
|
2. Docker tests, which test a complete Docker image, using the Docker API
|
||||||
|
|
||||||
|
### Running the Docker tests
|
||||||
|
|
||||||
|
The Docker tests can be run locally on a machine with Docker. For example:
|
||||||
|
|
||||||
|
```
|
||||||
|
make test-devserver
|
||||||
|
make test-advancedserver
|
||||||
|
```
|
||||||
|
|
||||||
|
You can specify the image to use directly by using the `MQ_IMAGE_ADVANCEDSERVER` or `MQ_IMAGE_DEVSERVER` variables, for example:
|
||||||
|
|
||||||
|
```
|
||||||
|
MQ_IMAGE_ADVANCEDSERVER=ibm-mqadvanced-server:9.4.1.0-amd64 make test-advancedserver
|
||||||
|
```
|
||||||
|
|
||||||
|
You can pass parameters to `go test` with an environment variable. For example, to run the "TestGoldenPath" test, run the following command:
|
||||||
|
|
||||||
|
```
|
||||||
|
TEST_OPTS_DOCKER="-run TestGoldenPath" make test-advancedserver
|
||||||
|
```
|
||||||
|
|
||||||
|
You can enable additional tracing of container engine commands by setting `TEST_LOG_CONTAINER_COMMANDS` to `true`:
|
||||||
|
|
||||||
|
```
|
||||||
|
TEST_LOG_CONTAINER_COMMANDS="true" make test-advancedserver
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also use the same environment variables you specified when [building](./building), for example, the following will try and test an image called `ibm-mqadvanced-server:9.2.0.0-amd64`:
|
||||||
|
|
||||||
|
```
|
||||||
|
MQ_VERSION=9.2.0.0 make test-advancedserver
|
||||||
|
```
|
||||||
|
|
||||||
|
### Running the Docker tests with code coverage
|
||||||
|
You can produce code coverage results from the Docker tests by running the following:
|
||||||
|
|
||||||
|
```
|
||||||
|
make build-advancedserver-cover
|
||||||
|
make test-advancedserver-cover
|
||||||
|
```
|
||||||
|
|
||||||
|
In order to generate code coverage metrics from the Docker tests, the build step creates a new Docker image with an instrumented version of the code. Each test is then run individually, producing a coverage report each under `test/docker/coverage/`. These individual reports are then combined. The combined report is written to the `coverage` directory.
|
||||||
17
docs/troubleshooting.md
Normal file
17
docs/troubleshooting.md
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# Troubleshooting
|
||||||
|
|
||||||
|
## AMQ7017: Log not available
|
||||||
|
If you see this message in the container logs, it means that the directory being used for the container's volume doesn't use a filesystem supported by IBM MQ. To solve this, you need to make sure the container's `/mnt/mqm` volume is put on a supported filesystem. The best way to do this is to use [Docker volumes](https://docs.docker.com/storage/volumes/), instead of bind-mounted directories.
|
||||||
|
|
||||||
|
## Container command not found or does not exist
|
||||||
|
This message also appears as "System error: no such file or directory" in some versions of Docker. This can happen using a Docker client on Windows, and is related to line-ending characters. When you clone the Git repository on Windows, Git is often configured to convert any UNIX-style LF line-endings to Windows-style CRLF line-endings. Files with these line-endings end up in the built Docker image, and cause the container to fail at start-up. One solution to this problem is to stop Git from converting the line-ending characters, with the following command:
|
||||||
|
|
||||||
|
```
|
||||||
|
git config --global core.autocrlf input
|
||||||
|
```
|
||||||
|
|
||||||
|
## Old Linux kernel versions
|
||||||
|
MQ works best if you have a Linux kernel version of V3.16 or higher (run `uname -r` to check).
|
||||||
|
|
||||||
|
If you have an older version, you might need to add the [`--ipc host`](https://docs.docker.com/engine/reference/run/#ipc-settings-ipc) option when you run an MQ container. The reason for this is that IBM MQ uses shared memory, and on Linux kernels prior to V3.16, containers are usually limited to 32 MB of shared memory. In a [change](https://git.kernel.org/cgit/linux/kernel/git/mhocko/mm.git/commit/include/uapi/linux/shm.h?id=060028bac94bf60a65415d1d55a359c3a17d5c31
|
||||||
|
) to Linux kernel V3.16, the hard-coded limit is greatly increased. This kernel version is available in Ubuntu 14.04.2 onwards, Fedora V20 onwards, and boot2docker V1.2 onwards. Some Linux distributions, like Red Hat Enterprise Linux, patch older kernel versions, so you might find that the patch has been applied already, even if you see a lower kernel version number. If you are using a host with an older kernel version, then you can still run MQ, but you have to give it access to the host's IPC namespace using the [`--ipc host`](https://docs.docker.com/engine/reference/run/#ipc-settings-ipc) option on `docker run`. Note that this reduces the security isolation of your container.
|
||||||
146
docs/usage.md
Normal file
146
docs/usage.md
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
# Usage
|
||||||
|
|
||||||
|
In order to use the image, it is necessary to accept the terms of the IBM MQ license. This is achieved by specifying the environment variable `LICENSE` equal to `accept` when running the image. You can also view the license terms by setting this variable to `view`. Failure to set the variable will result in the termination of the container with a usage statement. You can view the license in a different language by also setting the `LANG` environment variable.
|
||||||
|
|
||||||
|
> **Note**: You can use `podman` instead of `docker` in any of the examples on this page.
|
||||||
|
|
||||||
|
## Running with the default configuration
|
||||||
|
You can run a queue manager with the default configuration and a listener on port 1414 using the following command. For example, the following command creates and starts a queue manager called `QM1`, and maps port 1414 on the host to the MQ listener on port 1414 inside the container, as well as port 9443 on the host to the web console on port 9443 inside the container:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
docker run \
|
||||||
|
--env LICENSE=accept \
|
||||||
|
--env MQ_QMGR_NAME=QM1 \
|
||||||
|
--publish 1414:1414 \
|
||||||
|
--publish 9443:9443 \
|
||||||
|
--detach \
|
||||||
|
icr.io/ibm-messaging/mq
|
||||||
|
```
|
||||||
|
|
||||||
|
## Running with the default configuration and a volume
|
||||||
|
The above example will not persist any configuration data or messages across container runs. In order to do this, you need to use a [volume](https://docs.docker.com/storage/volumes/). For example, you can create a volume with the following command:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
docker volume create qm1data
|
||||||
|
```
|
||||||
|
|
||||||
|
You can then run a queue manager using this volume as follows:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
docker run \
|
||||||
|
--env LICENSE=accept \
|
||||||
|
--env MQ_QMGR_NAME=QM1 \
|
||||||
|
--publish 1414:1414 \
|
||||||
|
--publish 9443:9443 \
|
||||||
|
--detach \
|
||||||
|
--volume qm1data:/mnt/mqm \
|
||||||
|
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.
|
||||||
|
|
||||||
|
## Running with the default configuration and Prometheus metrics enabled
|
||||||
|
You can run a queue manager with [Prometheus](https://prometheus.io) metrics enabled. The following command will generate Prometheus metrics for your queue manager on `/metrics` port `9157`:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
docker run \
|
||||||
|
--env LICENSE=accept \
|
||||||
|
--env MQ_QMGR_NAME=QM1 \
|
||||||
|
--env MQ_ENABLE_METRICS=true \
|
||||||
|
--publish 1414:1414 \
|
||||||
|
--publish 9443:9443 \
|
||||||
|
--publish 9157:9157 \
|
||||||
|
--detach \
|
||||||
|
icr.io/ibm-messaging/mq
|
||||||
|
```
|
||||||
|
|
||||||
|
## Customizing the queue manager configuration
|
||||||
|
|
||||||
|
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
|
||||||
|
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/docs/en/ibm-mq/9.4?topic=administering-working-remote-mq-objects), 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.
|
||||||
|
|
||||||
|
The following is an *example* `Dockerfile` for creating your own pre-configured image, which adds a custom MQ configuration file:
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
FROM icr.io/ibm-messaging/mq
|
||||||
|
USER 1001
|
||||||
|
COPY 20-config.mqsc /etc/mqm/
|
||||||
|
```
|
||||||
|
|
||||||
|
Here is an example corresponding `20-config.mqsc` script, which creates two local queues:
|
||||||
|
|
||||||
|
```mqsc
|
||||||
|
DEFINE QLOCAL(MY.QUEUE.1) REPLACE
|
||||||
|
DEFINE QLOCAL(MY.QUEUE.2) REPLACE
|
||||||
|
```
|
||||||
|
|
||||||
|
The file `20-config.mqsc` should be saved into the same directory as the `Dockerfile`.
|
||||||
|
|
||||||
|
## Running MQ commands
|
||||||
|
It is recommended that you configure MQ in your own custom image. However, you may need to run MQ commands directly inside the process space of the container. To run a command against a running queue manager, you can use `docker exec`, for example:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
docker exec \
|
||||||
|
--tty \
|
||||||
|
--interactive \
|
||||||
|
${CONTAINER_ID} \
|
||||||
|
dspmq
|
||||||
|
```
|
||||||
|
|
||||||
|
Using this technique, you can have full control over all aspects of the MQ installation. Note that if you use this technique to make changes to the filesystem, then those changes would be lost if you re-created your container unless you make those changes in volumes.
|
||||||
|
|
||||||
|
## Supplying TLS certificates
|
||||||
|
|
||||||
|
If you wish to supply TLS Certificates that the queue manager and MQ Console should use for TLS operations then you must supply a PKCS#1 or unencrypted PKCS#8 PEM files for both the certificates and private keys in the following directories:
|
||||||
|
|
||||||
|
* `/etc/mqm/pki/keys/<Label>` - for certificates with public and private keys
|
||||||
|
* `/etc/mqm/pki/trust/<index>` - for certificates with only the public key
|
||||||
|
|
||||||
|
For example, if you have an identity certificate you wish to add with the label `mykey` and 2 certificates you wish to add as trusted then you would need to add the files into the following locations where files ending in `.key` contain private keys and `.crt` contain certificates:
|
||||||
|
|
||||||
|
- `/etc/mqm/pki/keys/mykey/tls.key`
|
||||||
|
- `/etc/mqm/pki/keys/mykey/tls.crt`
|
||||||
|
- `/etc/mqm/pki/keys/mykey/ca.crt`
|
||||||
|
- `/etc/mqm/pki/trust/0/tls.crt`
|
||||||
|
- `/etc/mqm/pki/trust/1/tls.crt`
|
||||||
|
|
||||||
|
This can be achieved by either mounting the directories or files into the container when you run it or by baking the files into the correct location in the image.
|
||||||
|
|
||||||
|
If you supply multiple identity certificates then the first label alphabetically will be chosen as the certificate to be used by the MQ Console and the default certificate for the queue manager. If you wish to use a different certificate on the queue manager then you can change the certificate to use at runtime by executing the MQSC command `ALTER QMGR CERTLABL('<newlabel>')`
|
||||||
|
|
||||||
|
It must be noted that queue manager certificate with a Subject Distinguished Name (DN) same as it's Issuer certificate (CA) is not supported. Certificates must have a unique Subject Distinguished Name.
|
||||||
|
|
||||||
|
## Running with a read-only root filesystem
|
||||||
|
Starting with version 9.3.4.0, you can run MQ container with a read-only root filesystem. In order to do this, you need to mount three [volumes](https://docs.docker.com/storage/volumes/) into the MQ container, one for queue manager data, one for `run` directory that will contain files used for queue manager configuration and one for `tmp` directory that will be used for collecting diagnostic data. You also need specify `--read-only` parameter while starting the container. Following describes the steps to run MQ container with a read-only root filesystem.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
docker volume create qm1data
|
||||||
|
```
|
||||||
|
|
||||||
|
```sh
|
||||||
|
docker volume create run
|
||||||
|
```
|
||||||
|
|
||||||
|
```sh
|
||||||
|
docker volume create tmp
|
||||||
|
```
|
||||||
|
|
||||||
|
You can then run a queue manager with a read-only root filesystem as follows:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
docker run \
|
||||||
|
--env LICENSE=accept \
|
||||||
|
--env MQ\_QMGR\_NAME=QM1 \
|
||||||
|
--mount type=volume,source=run,destination=/run \
|
||||||
|
--mount type=volume,source=tmp,destination=/tmp \
|
||||||
|
--mount type=volume,source=qm1data,destination=/mnt/mqm \
|
||||||
|
--read-only \
|
||||||
|
--publish 1414:1414 \
|
||||||
|
--detach \
|
||||||
|
icr.io/ibm-messaging/mq
|
||||||
|
```
|
||||||
47
download-basemq.sh
Executable file
47
download-basemq.sh
Executable file
@@ -0,0 +1,47 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# © Copyright IBM Corporation 2024
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
while getopts r:v: flag
|
||||||
|
do
|
||||||
|
case "${flag}" in
|
||||||
|
r) MQ_ARCHIVE_DEV=${OPTARG};;
|
||||||
|
v) MQ_VERSION_VRM=${OPTARG};;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ -z $MQ_ARCHIVE_DEV || -z $MQ_VERSION_VRM ]] ; then
|
||||||
|
printf "${ERROR}MQ driver download script parameters missing!${END}\n"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
BASE_MQ_LOCATION="https://public.dhe.ibm.com/ibmdl/export/pub/software/websphere/messaging/mqadv"
|
||||||
|
|
||||||
|
FILE_FOUND=$(curl -I $BASE_MQ_LOCATION/$MQ_ARCHIVE_DEV -w "%{http_code}" -s -o /dev/null)
|
||||||
|
|
||||||
|
if [ "$FILE_FOUND" -eq 200 ]; then
|
||||||
|
curl --fail --location $BASE_MQ_LOCATION/$MQ_ARCHIVE_DEV --output downloads/"$MQ_ARCHIVE_DEV"
|
||||||
|
|
||||||
|
elif [ "$FILE_FOUND" -eq 404 ]; then
|
||||||
|
curl -s --list-only --location $BASE_MQ_LOCATION | sed 's/href=/\nhref=/g' |grep href=\" |sed 's/.*href="//g;s/".*//g' > downloads/base-mq-file-list.txt
|
||||||
|
echo "$MQ_ARCHIVE_DEV is not available at $BASE_MQ_LOCATION" && echo "================================================="
|
||||||
|
grep "$MQ_VERSION_VRM" downloads/base-mq-file-list.txt| grep "IBM-MQ-" && echo "=================================================" && echo "$MQ_VERSION_VRM images available in the download site are listed above"
|
||||||
|
echo "Choose any of the available version and run build command for example,'MQ_VERSION=9.4.0.0 make build-devserver'"
|
||||||
|
rm -f downloads/base-mq-file-list.txt
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
echo "Unexpected error when downloading MQ driver from $BASE_MQ_LOCATION"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
20
etc/mqm/15-tls.mqsc.tpl
Normal file
20
etc/mqm/15-tls.mqsc.tpl
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
* © Copyright IBM Corporation 2019, 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.
|
||||||
|
|
||||||
|
* Set the keystore location for the queue manager
|
||||||
|
ALTER QMGR SSLKEYR('{{ .SSLKeyR }}')
|
||||||
|
ALTER QMGR CERTLABL('{{ .CertificateLabel }}')
|
||||||
|
ALTER QMGR SSLFIPS({{ .SSLFips }})
|
||||||
|
REFRESH SECURITY(*) TYPE(SSL)
|
||||||
11
etc/mqm/qm-service-component.ini.default
Normal file
11
etc/mqm/qm-service-component.ini.default
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
ServiceComponent:
|
||||||
|
Service=AuthorizationService
|
||||||
|
Name=Dev.HtpAuth.Service
|
||||||
|
Module=/opt/mqm/lib64/mqsimpleauth.so
|
||||||
|
ComponentDataSize=0
|
||||||
|
ServiceComponent:
|
||||||
|
Service=AuthorizationService
|
||||||
|
Name=MQSeries.UNIX.auth.service
|
||||||
|
Module=amqzfu
|
||||||
|
ComponentDataSize=0
|
||||||
|
|
||||||
23
go.mod
Normal file
23
go.mod
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
module github.com/ibm-messaging/mq-container
|
||||||
|
|
||||||
|
go 1.19
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/ibm-messaging/mq-golang v2.0.0+incompatible
|
||||||
|
github.com/prometheus/client_golang v1.13.0
|
||||||
|
github.com/prometheus/client_model v0.2.0
|
||||||
|
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635
|
||||||
|
golang.org/x/sys v0.15.0
|
||||||
|
software.sslmate.com/src/go-pkcs12 v0.4.0
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
|
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||||
|
github.com/golang/protobuf v1.5.2 // indirect
|
||||||
|
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
||||||
|
github.com/prometheus/common v0.37.0 // indirect
|
||||||
|
github.com/prometheus/procfs v0.8.0 // indirect
|
||||||
|
golang.org/x/crypto v0.17.0 // indirect
|
||||||
|
google.golang.org/protobuf v1.33.0 // indirect
|
||||||
|
)
|
||||||
484
go.sum
Normal file
484
go.sum
Normal file
@@ -0,0 +1,484 @@
|
|||||||
|
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
|
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
|
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||||
|
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||||
|
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||||
|
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
||||||
|
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
||||||
|
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
|
||||||
|
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
|
||||||
|
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
|
||||||
|
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
|
||||||
|
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
|
||||||
|
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
|
||||||
|
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
|
||||||
|
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
|
||||||
|
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||||
|
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||||
|
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
||||||
|
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
|
||||||
|
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
|
||||||
|
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
|
||||||
|
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||||
|
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||||
|
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||||
|
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
||||||
|
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
||||||
|
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
|
||||||
|
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||||
|
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
|
||||||
|
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
||||||
|
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
||||||
|
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||||
|
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||||
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
|
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||||
|
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/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
|
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
|
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
|
||||||
|
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
|
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||||
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||||
|
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||||
|
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/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/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/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||||
|
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||||
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
|
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-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
|
||||||
|
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-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
|
||||||
|
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/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
|
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
|
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
|
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
|
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
|
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
|
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||||
|
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||||
|
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||||
|
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||||
|
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
||||||
|
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.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||||
|
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||||
|
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
|
||||||
|
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/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
|
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
|
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.4.1/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.1/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/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
|
||||||
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
|
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||||
|
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||||
|
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||||
|
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||||
|
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
|
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
|
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
|
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
|
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
|
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||||
|
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||||
|
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||||
|
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
|
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
|
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||||
|
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/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||||
|
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||||
|
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||||
|
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/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
|
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/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||||
|
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.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
|
||||||
|
github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
|
||||||
|
github.com/prometheus/client_golang v1.13.0 h1:b71QUfeo5M8gq2+evJdTPfZhYMAU0uKPkyPJ7TPsloU=
|
||||||
|
github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ=
|
||||||
|
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.0.0-20190812154241-14fe0d1b01d4/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/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
|
||||||
|
github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
|
||||||
|
github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE=
|
||||||
|
github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=
|
||||||
|
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/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||||
|
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||||
|
github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo=
|
||||||
|
github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
|
||||||
|
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
|
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=
|
||||||
|
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||||
|
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||||
|
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
|
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
|
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
|
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-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
|
||||||
|
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
||||||
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||||
|
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
|
||||||
|
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
||||||
|
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||||
|
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||||
|
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||||
|
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||||
|
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||||
|
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||||
|
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
|
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-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
|
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
||||||
|
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||||
|
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||||
|
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||||
|
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||||
|
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||||
|
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||||
|
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
|
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
|
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-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-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-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||||
|
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/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-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
|
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
|
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
|
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
|
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
|
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
|
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
|
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
|
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
|
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||||
|
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
|
||||||
|
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-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/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-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-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-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200523222454-059865788121/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-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
||||||
|
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
|
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
|
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
golang.org/x/tools v0.0.0-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-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
|
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
|
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
|
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
|
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
|
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
|
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||||
|
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||||
|
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
||||||
|
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
|
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
|
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
|
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
|
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||||
|
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||||
|
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||||
|
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/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||||
|
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||||
|
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||||
|
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||||
|
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||||
|
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||||
|
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||||
|
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||||
|
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||||
|
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||||
|
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||||
|
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||||
|
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||||
|
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||||
|
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
|
||||||
|
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
|
||||||
|
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/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||||
|
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||||
|
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||||
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
|
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||||
|
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||||
|
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
||||||
|
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||||
|
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||||
|
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||||
|
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||||
|
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||||
|
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||||
|
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
|
||||||
|
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
|
||||||
|
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||||
|
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
|
||||||
|
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
|
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
|
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
|
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||||
|
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||||
|
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.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||||
|
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||||
|
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||||
|
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
|
||||||
|
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
||||||
|
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||||
|
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||||
|
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.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||||
|
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.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||||
|
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||||
|
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||||
|
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=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||||
|
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||||
|
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||||
|
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||||
|
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||||
|
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||||
|
software.sslmate.com/src/go-pkcs12 v0.4.0 h1:H2g08FrTvSFKUj+D309j1DPfk5APnIdAQAB8aEykJ5k=
|
||||||
|
software.sslmate.com/src/go-pkcs12 v0.4.0/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI=
|
||||||
5
ha/10-native-ha-instance.ini.tpl
Normal file
5
ha/10-native-ha-instance.ini.tpl
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
NativeHALocalInstance:
|
||||||
|
Name={{ .Name }}
|
||||||
|
{{ if .SSLFipsRequired }}
|
||||||
|
SSLFipsRequired={{ .SSLFipsRequired }}
|
||||||
|
{{- end}}
|
||||||
8
ha/10-native-ha-keystore.ini.tpl
Normal file
8
ha/10-native-ha-keystore.ini.tpl
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
NativeHALocalInstance:
|
||||||
|
{{ if .CertificateLabel }}
|
||||||
|
CertificateLabel={{ .CertificateLabel }}
|
||||||
|
{{- end }}
|
||||||
|
{{ if .Group.CertificateLabel }}
|
||||||
|
GroupCertificateLabel={{ .Group.CertificateLabel}}
|
||||||
|
{{- end }}
|
||||||
|
KeyRepository={{ .KeyRepository }}
|
||||||
29
ha/10-native-ha.ini.tpl
Normal file
29
ha/10-native-ha.ini.tpl
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
NativeHALocalInstance:
|
||||||
|
{{ if .ShouldConfigureTLS }}
|
||||||
|
{{ if .CipherSpec }}
|
||||||
|
CipherSpec={{ .CipherSpec }}
|
||||||
|
{{- end }}
|
||||||
|
{{ if .Group.Local.Name }}
|
||||||
|
GroupName={{ .Group.Local.Name }}
|
||||||
|
{{- end}}
|
||||||
|
{{ if .Group.CipherSpec }}
|
||||||
|
GroupCipherSpec={{ .Group.CipherSpec }}
|
||||||
|
{{- end }}
|
||||||
|
{{ if .Group.Local.Role }}
|
||||||
|
GroupRole={{ .Group.Local.Role }}
|
||||||
|
{{- end}}
|
||||||
|
{{ if .Group.Local.Address }}
|
||||||
|
GroupLocalAddress={{ .Group.Local.Address }}
|
||||||
|
{{- end}}
|
||||||
|
{{- end }}{{/* end if .ShouldConfigureTLS */}}
|
||||||
|
{{- range $idx, $instance := .Instances}}
|
||||||
|
NativeHAInstance:
|
||||||
|
Name={{ $instance.Name }}
|
||||||
|
ReplicationAddress={{ $instance.ReplicationAddress }}
|
||||||
|
{{- end}}
|
||||||
|
{{ if .Group.Recovery.Name }}
|
||||||
|
NativeHARecoveryGroup:
|
||||||
|
GroupName={{ .Group.Recovery.Name }}
|
||||||
|
Enabled={{ .Group.Recovery.Enabled }}
|
||||||
|
ReplicationAddress={{ .Group.Recovery.Address }}
|
||||||
|
{{- end }}
|
||||||
32
incubating/Dockerfile-sfbridge
Normal file
32
incubating/Dockerfile-sfbridge
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
# © Copyright IBM Corporation 2015, 2019
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
FROM ubuntu:16.04
|
||||||
|
|
||||||
|
# The URL to download the MQ installer from in tar.gz format
|
||||||
|
ARG MQ_URL=https://public.dhe.ibm.com/ibmdl/export/pub/software/websphere/messaging/mqadv/mqadv_dev911_ubuntu_x86-64.tar.gz
|
||||||
|
|
||||||
|
# The MQ packages to install
|
||||||
|
ARG MQ_PACKAGES="ibmmq-sfbridge"
|
||||||
|
|
||||||
|
ARG MQM_UID=999
|
||||||
|
|
||||||
|
ADD install-mq.sh /usr/local/bin/
|
||||||
|
RUN chmod u+x /usr/local/bin/install-mq.sh \
|
||||||
|
&& install-mq.sh $MQM_UID
|
||||||
|
|
||||||
|
ENV LANG=en_US.UTF-8
|
||||||
|
|
||||||
|
# TODO: Create configuration file from environment variables?
|
||||||
|
# TODO: Add entrypoint to run the bridge
|
||||||
51
incubating/mqadvanced-server-dev/10-dev.mqsc.tpl
Normal file
51
incubating/mqadvanced-server-dev/10-dev.mqsc.tpl
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
* © Copyright IBM Corporation 2017, 2024
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
|
||||||
|
* Developer queues
|
||||||
|
DEFINE QLOCAL('DEV.QUEUE.1') REPLACE
|
||||||
|
DEFINE QLOCAL('DEV.QUEUE.2') REPLACE
|
||||||
|
DEFINE QLOCAL('DEV.QUEUE.3') REPLACE
|
||||||
|
DEFINE QLOCAL('DEV.DEAD.LETTER.QUEUE') REPLACE
|
||||||
|
DEFINE QMODEL('DEV.APP.MODEL.QUEUE') REPLACE
|
||||||
|
|
||||||
|
* Use a different dead letter queue, for undeliverable messages
|
||||||
|
ALTER QMGR DEADQ('DEV.DEAD.LETTER.QUEUE')
|
||||||
|
|
||||||
|
* Developer topics
|
||||||
|
DEFINE TOPIC('DEV.BASE.TOPIC') TOPICSTR('dev/') REPLACE
|
||||||
|
|
||||||
|
* Developer connection authentication
|
||||||
|
DEFINE AUTHINFO('DEV.AUTHINFO') AUTHTYPE(IDPWOS) CHCKCLNT(REQDADM) CHCKLOCL(OPTIONAL) ADOPTCTX(YES) REPLACE
|
||||||
|
ALTER QMGR CONNAUTH('DEV.AUTHINFO')
|
||||||
|
REFRESH SECURITY(*) TYPE(CONNAUTH)
|
||||||
|
|
||||||
|
* Developer channels (Application + Admin)
|
||||||
|
* Developer channels (Application + Admin)
|
||||||
|
DEFINE CHANNEL('DEV.ADMIN.SVRCONN') CHLTYPE(SVRCONN) REPLACE
|
||||||
|
DEFINE CHANNEL('DEV.APP.SVRCONN') CHLTYPE(SVRCONN) MCAUSER('app') REPLACE
|
||||||
|
|
||||||
|
* Developer channel authentication rules
|
||||||
|
SET CHLAUTH('*') TYPE(ADDRESSMAP) ADDRESS('*') USERSRC(NOACCESS) DESCR('Back-stop rule - Blocks everyone') ACTION(REPLACE)
|
||||||
|
SET CHLAUTH('DEV.APP.SVRCONN') TYPE(ADDRESSMAP) ADDRESS('*') USERSRC(CHANNEL) CHCKCLNT({{ .ChckClnt }}) DESCR('Allows connection via APP channel') ACTION(REPLACE)
|
||||||
|
SET CHLAUTH('DEV.ADMIN.SVRCONN') TYPE(BLOCKUSER) USERLIST('nobody') DESCR('Allows admins on ADMIN channel') ACTION(REPLACE)
|
||||||
|
SET CHLAUTH('DEV.ADMIN.SVRCONN') TYPE(USERMAP) CLNTUSER('admin') USERSRC(CHANNEL) DESCR('Allows admin user to connect via ADMIN channel') ACTION(REPLACE)
|
||||||
|
SET CHLAUTH('DEV.ADMIN.SVRCONN') TYPE(USERMAP) CLNTUSER('admin') USERSRC(MAP) MCAUSER ('mqm') DESCR ('Allow admin as MQ-admin') ACTION(REPLACE)
|
||||||
|
|
||||||
|
* Developer authority records
|
||||||
|
SET AUTHREC PRINCIPAL('app') OBJTYPE(QMGR) AUTHADD(CONNECT,INQ)
|
||||||
|
SET AUTHREC PROFILE('DEV.**') PRINCIPAL('app') OBJTYPE(QUEUE) AUTHADD(BROWSE,GET,INQ,PUT)
|
||||||
|
SET AUTHREC PROFILE('DEV.**') PRINCIPAL('app') OBJTYPE(TOPIC) AUTHADD(PUB,SUB)
|
||||||
|
SET AUTHREC PROFILE('DEV.APP.MODEL.QUEUE') PRINCIPAL('app') OBJTYPE(QUEUE) AUTHADD(BROWSE,DSP,GET,INQ,PUT)
|
||||||
|
|
||||||
18
incubating/mqadvanced-server-dev/20-dev-tls.mqsc.tpl
Normal file
18
incubating/mqadvanced-server-dev/20-dev-tls.mqsc.tpl
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
* © Copyright IBM Corporation 2018, 2022
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
|
||||||
|
* Set the cipherspec for dev channels
|
||||||
|
ALTER CHANNEL('DEV.APP.SVRCONN') CHLTYPE(SVRCONN) SSLCIPH(ANY_TLS12_OR_HIGHER) SSLCAUTH(OPTIONAL)
|
||||||
|
ALTER CHANNEL('DEV.ADMIN.SVRCONN') CHLTYPE(SVRCONN) SSLCIPH(ANY_TLS12_OR_HIGHER) SSLCAUTH(OPTIONAL)
|
||||||
78
incubating/mqadvanced-server-dev/admin.json.tpl
Normal file
78
incubating/mqadvanced-server-dev/admin.json.tpl
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
{
|
||||||
|
"version": 0.1,
|
||||||
|
"tabs": [
|
||||||
|
{
|
||||||
|
"title": "IBM MQ Container",
|
||||||
|
"numColumns": 2,
|
||||||
|
"model": {
|
||||||
|
"title": "",
|
||||||
|
"rows": [
|
||||||
|
{
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"widgets": [
|
||||||
|
{
|
||||||
|
"type": "channel",
|
||||||
|
"config": {
|
||||||
|
"selectedQM": "{{ .QueueManagerName }}",
|
||||||
|
"showSysObjs": false,
|
||||||
|
"sizex": 1,
|
||||||
|
"sizey": 1,
|
||||||
|
"subType": "all"
|
||||||
|
},
|
||||||
|
"title": "Channels on {{ .QueueManagerName }}",
|
||||||
|
"titleTemplateUrl": "adf/templates/widget-title.html",
|
||||||
|
"gridsterrow": 0,
|
||||||
|
"gridstercol": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "topic",
|
||||||
|
"config": {
|
||||||
|
"selectedQM": "{{ .QueueManagerName }}",
|
||||||
|
"showSysObjs": false,
|
||||||
|
"sizex": 1,
|
||||||
|
"sizey": 1
|
||||||
|
},
|
||||||
|
"title": "Topics on {{ .QueueManagerName }}",
|
||||||
|
"titleTemplateUrl": "adf/templates/widget-title.html",
|
||||||
|
"gridsterrow": 1,
|
||||||
|
"gridstercol": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "queue",
|
||||||
|
"config": {
|
||||||
|
"selectedQM": "{{ .QueueManagerName }}",
|
||||||
|
"showSysObjs": false,
|
||||||
|
"sizex": 1,
|
||||||
|
"sizey": 1,
|
||||||
|
"subType": "all"
|
||||||
|
},
|
||||||
|
"title": "Queues on {{ .QueueManagerName }}",
|
||||||
|
"titleTemplateUrl": "adf/templates/widget-title.html",
|
||||||
|
"gridsterrow": 1,
|
||||||
|
"gridstercol": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "queuemanager",
|
||||||
|
"gridstercol": 0,
|
||||||
|
"gridsterrow": 0,
|
||||||
|
"config": {
|
||||||
|
"type": "local",
|
||||||
|
"sizex": 1,
|
||||||
|
"sizey": 1,
|
||||||
|
"customTitle": "Queue Manager"
|
||||||
|
},
|
||||||
|
"title": "Queue Manager",
|
||||||
|
"titleTemplateUrl": "adf/templates/widget-title.html"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"titleTemplateUrl": "adf/templates/dashboard-title.html"
|
||||||
|
},
|
||||||
|
"isMobile": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<server>
|
||||||
|
<featureManager>
|
||||||
|
<feature>appSecurity-2.0</feature>
|
||||||
|
<feature>basicAuthenticationMQ-1.0</feature>
|
||||||
|
</featureManager>
|
||||||
|
<enterpriseApplication id="com.ibm.mq.console">
|
||||||
|
<application-bnd>
|
||||||
|
<security-role name="MQWebAdmin">
|
||||||
|
<group name="MQWebUI" realm="defaultRealm"/>
|
||||||
|
</security-role>
|
||||||
|
</application-bnd>
|
||||||
|
</enterpriseApplication>
|
||||||
|
<enterpriseApplication id="com.ibm.mq.rest">
|
||||||
|
<application-bnd>
|
||||||
|
<security-role name="MQWebAdmin">
|
||||||
|
<group name="MQWebUI" realm="defaultRealm"/>
|
||||||
|
</security-role>
|
||||||
|
<security-role name="MQWebUser">
|
||||||
|
<group name="MQWebMessaging" realm="defaultRealm"/>
|
||||||
|
</security-role>
|
||||||
|
</application-bnd>
|
||||||
|
</enterpriseApplication>
|
||||||
|
<basicRegistry id="basic" realm="defaultRealm">
|
||||||
|
<user name="admin" password="${env.MQ_ADMIN_PASSWORD_SECURE}"/>
|
||||||
|
<user name="app" password="${env.MQ_APP_PASSWORD_SECURE}"/>
|
||||||
|
<group name="MQWebUI">
|
||||||
|
<member name="admin"/>
|
||||||
|
</group>
|
||||||
|
<group name="MQWebMessaging">
|
||||||
|
<member name="app"/>
|
||||||
|
</group>
|
||||||
|
</basicRegistry>
|
||||||
|
<variable name="httpHost" value="*"/>
|
||||||
|
<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}"/>
|
||||||
|
<httpDispatcher enableWelcomePage="false" appOrContextRootMissingMessage='<script>document.location.href="/ibmmq/console/";</script>' />
|
||||||
|
<include location="tls.xml"/>
|
||||||
|
</server>
|
||||||
26
incubating/mqipt/Dockerfile
Normal file
26
incubating/mqipt/Dockerfile
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
# © Copyright IBM Corporation 2018, 2023
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
FROM registry.access.redhat.com/ubi8/ubi-minimal
|
||||||
|
ARG MQIPT_ARCHIVE=./IBM-MQIPT-LinuxX64.tar
|
||||||
|
RUN microdnf --disableplugin=subscription-manager install bash grep procps-ng sed which
|
||||||
|
ADD $MQIPT_ARCHIVE /opt
|
||||||
|
COPY startMQIPT.sh /usr/local/bin
|
||||||
|
ENV MQIPT_PATH=/opt/mqipt
|
||||||
|
RUN chown -R 1001:0 $MQIPT_PATH \
|
||||||
|
&& chown -R 1001:0 /usr/local/bin/startMQIPT.sh \
|
||||||
|
&& chmod -R 550 /usr/local/bin/startMQIPT.sh
|
||||||
|
VOLUME /var/mqipt
|
||||||
|
USER 1001
|
||||||
|
ENTRYPOINT ["startMQIPT.sh"]
|
||||||
41
incubating/mqipt/README.md
Normal file
41
incubating/mqipt/README.md
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
# IBM MQ Internet Pass-Thru in a container
|
||||||
|
|
||||||
|
IBM® MQ Internet Pass-Thru (MQIPT) is an optional component of IBM MQ. MQIPT runs as a stand-alone service that can receive and forward IBM MQ message flows, either between two IBM MQ queue managers, or between an IBM MQ client and an IBM MQ queue manager.
|
||||||
|
MQIPT enables this connection when the client and server are not on the same physical network.
|
||||||
|
|
||||||
|
This repository contains all the resources that you will need to create a container image that contains MQIPT.
|
||||||
|
|
||||||
|
## How to build this image
|
||||||
|
|
||||||
|
1. Download MQIPT for Linux x86_64 from [Fix Central](https://ibm.biz/mq93ipt). The name of the download file is similar to `9.3.x.x-IBM-MQIPT-LinuxX64.tar.gz`.
|
||||||
|
2. Ensure the MQIPT downloaded tar file is available in this directory.
|
||||||
|
3. Run the following command in this directory to build the container image:
|
||||||
|
`docker build --build-arg MQIPT_ARCHIVE=<tar_file_name> -t mqipt .`
|
||||||
|
|
||||||
|
Once the build has completed you will have a new container image called `mqipt:latest` which contains MQIPT.
|
||||||
|
|
||||||
|
## How to run this image
|
||||||
|
|
||||||
|
Before you run the MQIPT container image you should understand how MQIPT operates. You can read about MQIPT in the [IBM MQ documentation](https://www.ibm.com/docs/en/ibm-mq/9.3?topic=overview-mq-internet-pass-thru).
|
||||||
|
|
||||||
|
1. Create a MQIPT home directory that can be [mounted to a container](https://docs.docker.com/storage/). The MQIPT home directory contains configuration files and log files that are produced when MQIPT runs.
|
||||||
|
2. Create your [MQIPT configuration file](https://www.ibm.com/docs/en/ibm-mq/9.3?topic=reference-mq-internet-pass-thru-configuration) in the MQIPT home directory. This file **must** be called `mqipt.conf`. A sample configuration file is supplied with MQIPT in `samples/mqiptSample.conf`.
|
||||||
|
3. Run the following command to start a container with your built MQIPT image:
|
||||||
|
`docker run -d --volume <mqiptHome>:/var/mqipt -p <hostPort>:<containerPort> mqipt`
|
||||||
|
where `mqiptHome` is the MQIPT home directory you created in step 1, and `containerPort` is a port to be exposed that MQIPT is listening on, such as a route port.
|
||||||
|
|
||||||
|
If you want the container ports to be accessible outside of the host you must expose the required ports. This maps the container port to a port on the host, so that you can connect to the port on the host and access MQIPT. You might need to provide more than one `-p` parameters to expose all the ports that are required by your MQIPT configuration. **Note:** These ports must be available on the host. If the ports are not available, the Docker container will not start.
|
||||||
|
|
||||||
|
For more information about how to expose container ports, see [Docker Run reference](https://docs.docker.com/engine/reference/run/#expose-incoming-ports).
|
||||||
|
|
||||||
|
## Further information
|
||||||
|
|
||||||
|
For more information about MQIPT, see MQIPT documentation in the [IBM MQ documentation](https://www.ibm.com/docs/en/ibm-mq/9.3?topic=overview-mq-internet-pass-thru).
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
The Dockerfile and associated code and scripts are provided as-is and licensed under the [Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0.html).
|
||||||
|
|
||||||
|
## Copyright
|
||||||
|
|
||||||
|
© Copyright IBM Corporation 2018, 2023
|
||||||
28
incubating/mqipt/startMQIPT.sh
Normal file
28
incubating/mqipt/startMQIPT.sh
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# -*- mode: sh -*-
|
||||||
|
# © Copyright IBM Corporation 2018, 2023
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
stop()
|
||||||
|
{
|
||||||
|
/opt/mqipt/bin/mqiptAdmin -stop -n ipt1
|
||||||
|
}
|
||||||
|
|
||||||
|
trap stop SIGTERM SIGINT
|
||||||
|
|
||||||
|
# Run MQIPT and then wait on the process to end.
|
||||||
|
/opt/mqipt/bin/mqipt /var/mqipt -n ipt1 &
|
||||||
|
|
||||||
|
MQIPTPROCESS=$!
|
||||||
|
wait "$MQIPTPROCESS"
|
||||||
28
install-build-deps.sh
Executable file
28
install-build-deps.sh
Executable file
@@ -0,0 +1,28 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# -*- mode: sh -*-
|
||||||
|
# © Copyright IBM Corporation 2015, 2019
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
# Install Docker and dep, required by build (assumes Ubuntu host, as used by Travis build)
|
||||||
|
|
||||||
|
set -ex
|
||||||
|
|
||||||
|
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 apt-get update || :
|
||||||
|
sudo apt-get install -y jq
|
||||||
|
|
||||||
|
go install golang.org/x/lint/golint@latest
|
||||||
|
curl -sfL https://raw.githubusercontent.com/securego/gosec/master/install.sh | sh -s -- -b $GOPATH/bin v2.14.0 || echo "Gosec not installed. Platform may not be supported."
|
||||||
65
install-mq-server-prereqs.sh
Normal file
65
install-mq-server-prereqs.sh
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# -*- mode: sh -*-
|
||||||
|
# © Copyright IBM Corporation 2015, 2023
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
# Fail on any non-zero return code
|
||||||
|
set -ex
|
||||||
|
|
||||||
|
test -f /usr/bin/yum && YUM=true || YUM=false
|
||||||
|
test -f /usr/bin/microdnf && MICRODNF=true || MICRODNF=false
|
||||||
|
test -f /usr/bin/rpm && RPM=true || RPM=false
|
||||||
|
test -f /usr/bin/apt-get && UBUNTU=true || UBUNTU=false
|
||||||
|
CPU_ARCH=$(uname -m)
|
||||||
|
|
||||||
|
if ($UBUNTU); then
|
||||||
|
export DEBIAN_FRONTEND=noninteractive
|
||||||
|
# Use a reduced set of apt repositories.
|
||||||
|
# This ensures no unsupported code gets installed, and makes the build faster
|
||||||
|
source /etc/os-release
|
||||||
|
# Figure out the correct apt URL based on the CPU architecture
|
||||||
|
if [ "${CPU_ARCH}" == "x86_64" ]; then
|
||||||
|
APT_URL="http://archive.ubuntu.com/ubuntu/"
|
||||||
|
else
|
||||||
|
APT_URL="http://ports.ubuntu.com/ubuntu-ports/"
|
||||||
|
fi
|
||||||
|
# Use a reduced set of apt repositories.
|
||||||
|
# This ensures no unsupported code gets installed, and makes the build faster
|
||||||
|
echo "deb ${APT_URL} ${UBUNTU_CODENAME} main restricted" > /etc/apt/sources.list
|
||||||
|
echo "deb ${APT_URL} ${UBUNTU_CODENAME}-updates main restricted" >> /etc/apt/sources.list
|
||||||
|
echo "deb ${APT_URL} ${UBUNTU_CODENAME}-security main restricted" >> /etc/apt/sources.list
|
||||||
|
# Install additional packages required by MQ, this install process and the runtime scripts
|
||||||
|
EXTRA_DEBS="bash bc ca-certificates coreutils curl debianutils file findutils gawk grep libc-bin mount passwd procps sed tar util-linux"
|
||||||
|
apt-get update
|
||||||
|
apt-get install -y --no-install-recommends ${EXTRA_DEBS}
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ($RPM); then
|
||||||
|
EXTRA_RPMS="bash bc ca-certificates file findutils gawk glibc-common grep ncurses-libs passwd procps-ng sed shadow-utils tar util-linux which"
|
||||||
|
# Install additional packages required by MQ, this install process and the runtime scripts
|
||||||
|
$YUM && yum -y install --setopt install_weak_deps=false ${EXTRA_RPMS}
|
||||||
|
$MICRODNF && microdnf --disableplugin=subscription-manager -y install ${EXTRA_RPMS}
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Apply any bug fixes not included in base Ubuntu or MQ image.
|
||||||
|
# Don't upgrade everything based on Docker best practices https://docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices/#run
|
||||||
|
$UBUNTU && apt-get install -y libapparmor1 libsystemd0 systemd systemd-sysv libudev1 perl-base --only-upgrade
|
||||||
|
# End of bug fixes
|
||||||
|
|
||||||
|
# Clean up cached files
|
||||||
|
$UBUNTU && rm -rf /var/lib/apt/lists/*
|
||||||
|
$YUM && yum -y clean all
|
||||||
|
$YUM && rm -rf /var/cache/yum/*
|
||||||
|
$MICRODNF && microdnf --disableplugin=subscription-manager clean all
|
||||||
44
internal/command/command.go
Normal file
44
internal/command/command.go
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
© Copyright IBM Corporation 2017, 2022
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Package command contains code to run external commands
|
||||||
|
package command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"os/exec"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Run runs an OS command. On Linux it waits for the command to
|
||||||
|
// complete and returns the exit status (return code).
|
||||||
|
// Do not use this function to run shell built-ins (like "cd"), because
|
||||||
|
// the error handling works differently
|
||||||
|
func Run(name string, arg ...string) (string, int, error) {
|
||||||
|
return RunContext(context.Background(), name, arg...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func RunContext(ctx context.Context, name string, arg ...string) (string, int, error) {
|
||||||
|
// Run the command and wait for completion
|
||||||
|
// #nosec G204
|
||||||
|
cmd := exec.CommandContext(ctx, name, arg...)
|
||||||
|
out, err := cmd.CombinedOutput()
|
||||||
|
rc := cmd.ProcessState.ExitCode()
|
||||||
|
if err != nil {
|
||||||
|
return string(out), rc, fmt.Errorf("%v: %v", cmd.Path, err)
|
||||||
|
}
|
||||||
|
return string(out), rc, nil
|
||||||
|
}
|
||||||
47
internal/command/command_test.go
Normal file
47
internal/command/command_test.go
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
© Copyright IBM Corporation 2017
|
||||||
|
|
||||||
|
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 command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"runtime"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var commandTests = []struct {
|
||||||
|
name string
|
||||||
|
arg []string
|
||||||
|
rc int
|
||||||
|
}{
|
||||||
|
{"ls", []string{}, 0},
|
||||||
|
{"ls", []string{"madeup"}, 2},
|
||||||
|
{"bash", []string{"-c", "exit 99"}, 99},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRun(t *testing.T) {
|
||||||
|
if runtime.GOOS != "linux" {
|
||||||
|
t.Skip("Skipping tests for package which only works on Linux")
|
||||||
|
}
|
||||||
|
for _, table := range commandTests {
|
||||||
|
arg := table.arg
|
||||||
|
_, rc, err := Run(table.name, arg...)
|
||||||
|
if rc != table.rc {
|
||||||
|
t.Errorf("Run(%v,%v) - expected %v, got %v", table.name, table.arg, table.rc, rc)
|
||||||
|
}
|
||||||
|
if rc != 0 && err == nil {
|
||||||
|
t.Errorf("Run(%v,%v) - expected error for non-zero return code (rc=%v)", table.name, table.arg, rc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
287
internal/containerruntime/amicontained.go
Normal file
287
internal/containerruntime/amicontained.go
Normal file
@@ -0,0 +1,287 @@
|
|||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2018 Jessica Frazelle
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
The code for amicontained.go is forked from
|
||||||
|
https://github.com/genuinetools/bpfd/blob/434b609b3d4a5aeb461109b1167b68e000b72f69/proc/proc.go
|
||||||
|
|
||||||
|
The code was forked when the latest details are as "Latest commit 871fc34 on Sep 18, 2018"
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Adding IBM Copyright since the forked code had to be modified to remove deprecated ioutil package
|
||||||
|
/*
|
||||||
|
© Copyright IBM Corporation 2023
|
||||||
|
*/
|
||||||
|
|
||||||
|
package containerruntime
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/syndtr/gocapability/capability"
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ContainerRuntime is the type for the various container runtime strings.
|
||||||
|
type ContainerRuntime string
|
||||||
|
|
||||||
|
// SeccompMode is the type for the various seccomp mode strings.
|
||||||
|
type SeccompMode string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// RuntimeDocker is the string for the docker runtime.
|
||||||
|
RuntimeDocker ContainerRuntime = "docker"
|
||||||
|
// RuntimeRkt is the string for the rkt runtime.
|
||||||
|
RuntimeRkt ContainerRuntime = "rkt"
|
||||||
|
// RuntimeNspawn is the string for the systemd-nspawn runtime.
|
||||||
|
RuntimeNspawn ContainerRuntime = "systemd-nspawn"
|
||||||
|
// RuntimeLXC is the string for the lxc runtime.
|
||||||
|
RuntimeLXC ContainerRuntime = "lxc"
|
||||||
|
// RuntimeLXCLibvirt is the string for the lxc-libvirt runtime.
|
||||||
|
RuntimeLXCLibvirt ContainerRuntime = "lxc-libvirt"
|
||||||
|
// RuntimeOpenVZ is the string for the openvz runtime.
|
||||||
|
RuntimeOpenVZ ContainerRuntime = "openvz"
|
||||||
|
// RuntimeKubernetes is the string for the kubernetes runtime.
|
||||||
|
RuntimeKubernetes ContainerRuntime = "kube"
|
||||||
|
// RuntimeGarden is the string for the garden runtime.
|
||||||
|
RuntimeGarden ContainerRuntime = "garden"
|
||||||
|
// RuntimePodman is the string for the podman runtime.
|
||||||
|
RuntimePodman ContainerRuntime = "podman"
|
||||||
|
// RuntimeNotFound is the string for when no container runtime is found.
|
||||||
|
RuntimeNotFound ContainerRuntime = "not-found"
|
||||||
|
|
||||||
|
// SeccompModeDisabled is equivalent to "0" in the /proc/{pid}/status file.
|
||||||
|
SeccompModeDisabled SeccompMode = "disabled"
|
||||||
|
// SeccompModeStrict is equivalent to "1" in the /proc/{pid}/status file.
|
||||||
|
SeccompModeStrict SeccompMode = "strict"
|
||||||
|
// SeccompModeFiltering is equivalent to "2" in the /proc/{pid}/status file.
|
||||||
|
SeccompModeFiltering SeccompMode = "filtering"
|
||||||
|
|
||||||
|
apparmorUnconfined = "unconfined"
|
||||||
|
|
||||||
|
uint32Max = 4294967295
|
||||||
|
|
||||||
|
statusFileValue = ":(.*)"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ContainerRuntimes contains all the container runtimes.
|
||||||
|
ContainerRuntimes = []ContainerRuntime{
|
||||||
|
RuntimeDocker,
|
||||||
|
RuntimeRkt,
|
||||||
|
RuntimeNspawn,
|
||||||
|
RuntimeLXC,
|
||||||
|
RuntimeLXCLibvirt,
|
||||||
|
RuntimeOpenVZ,
|
||||||
|
RuntimeKubernetes,
|
||||||
|
RuntimeGarden,
|
||||||
|
RuntimePodman,
|
||||||
|
}
|
||||||
|
|
||||||
|
seccompModes = map[string]SeccompMode{
|
||||||
|
"0": SeccompModeDisabled,
|
||||||
|
"1": SeccompModeStrict,
|
||||||
|
"2": SeccompModeFiltering,
|
||||||
|
}
|
||||||
|
|
||||||
|
statusFileValueRegex = regexp.MustCompile(statusFileValue)
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetContainerRuntime returns the container runtime the process is running in.
|
||||||
|
// If pid is less than one, it returns the runtime for "self".
|
||||||
|
func GetContainerRuntime(tgid, pid int) ContainerRuntime {
|
||||||
|
file := "/proc/self/cgroup"
|
||||||
|
if pid > 0 {
|
||||||
|
if tgid > 0 {
|
||||||
|
file = fmt.Sprintf("/proc/%d/task/%d/cgroup", tgid, pid)
|
||||||
|
} else {
|
||||||
|
file = fmt.Sprintf("/proc/%d/cgroup", pid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// read the cgroups file
|
||||||
|
a := readFileString(file)
|
||||||
|
runtime := getContainerRuntime(a)
|
||||||
|
if runtime != RuntimeNotFound {
|
||||||
|
return runtime
|
||||||
|
}
|
||||||
|
|
||||||
|
// /proc/vz exists in container and outside of the container, /proc/bc only outside of the container.
|
||||||
|
if fileExists("/proc/vz") && !fileExists("/proc/bc") {
|
||||||
|
return RuntimeOpenVZ
|
||||||
|
}
|
||||||
|
|
||||||
|
a = os.Getenv("container")
|
||||||
|
runtime = getContainerRuntime(a)
|
||||||
|
if runtime != RuntimeNotFound {
|
||||||
|
return runtime
|
||||||
|
}
|
||||||
|
|
||||||
|
// PID 1 might have dropped this information into a file in /run.
|
||||||
|
// Read from /run/systemd/container since it is better than accessing /proc/1/environ,
|
||||||
|
// which needs CAP_SYS_PTRACE
|
||||||
|
a = readFileString("/run/systemd/container")
|
||||||
|
runtime = getContainerRuntime(a)
|
||||||
|
if runtime != RuntimeNotFound {
|
||||||
|
return runtime
|
||||||
|
}
|
||||||
|
|
||||||
|
return RuntimeNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
func getContainerRuntime(input string) ContainerRuntime {
|
||||||
|
if len(strings.TrimSpace(input)) < 1 {
|
||||||
|
return RuntimeNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, runtime := range ContainerRuntimes {
|
||||||
|
if strings.Contains(input, string(runtime)) {
|
||||||
|
return runtime
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return RuntimeNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
func fileExists(file string) bool {
|
||||||
|
if _, err := os.Stat(file); !os.IsNotExist(err) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func readFile(file string) []byte {
|
||||||
|
if !fileExists(file) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// filepath.clean was added to resolve the gosec build failure
|
||||||
|
// with error "Potential file inclusion via variable"
|
||||||
|
// IBM Modified the below line to remove the deprecated ioutil dependency
|
||||||
|
b, err := os.ReadFile(filepath.Clean(file))
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCapabilities returns the allowed capabilities for the process.
|
||||||
|
// If pid is less than one, it returns the capabilities for "self".
|
||||||
|
func GetCapabilities(pid int) (map[string][]string, error) {
|
||||||
|
allCaps := capability.List()
|
||||||
|
|
||||||
|
caps, err := capability.NewPid(pid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
allowedCaps := map[string][]string{}
|
||||||
|
allowedCaps["EFFECTIVE | PERMITTED | INHERITABLE"] = []string{}
|
||||||
|
allowedCaps["BOUNDING"] = []string{}
|
||||||
|
allowedCaps["AMBIENT"] = []string{}
|
||||||
|
|
||||||
|
for _, cap := range allCaps {
|
||||||
|
if caps.Get(capability.CAPS, cap) {
|
||||||
|
allowedCaps["EFFECTIVE | PERMITTED | INHERITABLE"] = append(allowedCaps["EFFECTIVE | PERMITTED | INHERITABLE"], cap.String())
|
||||||
|
}
|
||||||
|
if caps.Get(capability.BOUNDING, cap) {
|
||||||
|
allowedCaps["BOUNDING"] = append(allowedCaps["BOUNDING"], cap.String())
|
||||||
|
}
|
||||||
|
if caps.Get(capability.AMBIENT, cap) {
|
||||||
|
allowedCaps["AMBIENT"] = append(allowedCaps["AMBIENT"], cap.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return allowedCaps, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSeccompEnforcingMode returns the seccomp enforcing level (disabled, filtering, strict)
|
||||||
|
// for a process.
|
||||||
|
// If pid is less than one, it returns the seccomp enforcing mode for "self".
|
||||||
|
func GetSeccompEnforcingMode(pid int) SeccompMode {
|
||||||
|
file := "/proc/self/status"
|
||||||
|
if pid > 0 {
|
||||||
|
file = fmt.Sprintf("/proc/%d/status", pid)
|
||||||
|
}
|
||||||
|
|
||||||
|
return getSeccompEnforcingMode(readFileString(file))
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSeccompEnforcingMode(input string) SeccompMode {
|
||||||
|
mode := getStatusEntry(input, "Seccomp:")
|
||||||
|
sm, ok := seccompModes[mode]
|
||||||
|
if ok {
|
||||||
|
return sm
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pre linux 3.8, check if Seccomp is supported, via CONFIG_SECCOMP.
|
||||||
|
if err := unix.Prctl(unix.PR_GET_SECCOMP, 0, 0, 0, 0); err != unix.EINVAL {
|
||||||
|
// Make sure the kernel has CONFIG_SECCOMP_FILTER.
|
||||||
|
if err := unix.Prctl(unix.PR_SET_SECCOMP, unix.SECCOMP_MODE_FILTER, 0, 0, 0); err != unix.EINVAL {
|
||||||
|
return SeccompModeStrict
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return SeccompModeDisabled
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: make this function more efficient and read the file line by line.
|
||||||
|
func getStatusEntry(input, find string) string {
|
||||||
|
// Split status file string by line
|
||||||
|
statusMappings := strings.Split(input, "\n")
|
||||||
|
statusMappings = deleteEmpty(statusMappings)
|
||||||
|
|
||||||
|
for _, line := range statusMappings {
|
||||||
|
if strings.Contains(line, find) {
|
||||||
|
matches := statusFileValueRegex.FindStringSubmatch(line)
|
||||||
|
if len(matches) > 1 {
|
||||||
|
return strings.TrimSpace(matches[1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteEmpty(s []string) []string {
|
||||||
|
var r []string
|
||||||
|
for _, str := range s {
|
||||||
|
if strings.TrimSpace(str) != "" {
|
||||||
|
r = append(r, strings.TrimSpace(str))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func readFileString(file string) string {
|
||||||
|
b := readFile(file)
|
||||||
|
if b == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return strings.TrimSpace(string(b))
|
||||||
|
}
|
||||||
124
internal/containerruntime/runtime.go
Normal file
124
internal/containerruntime/runtime.go
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
/*
|
||||||
|
© Copyright IBM Corporation 2019,2023
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
package containerruntime
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func DetectContainerRuntime() ContainerRuntime {
|
||||||
|
return GetContainerRuntime(0, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetBaseImage() (string, error) {
|
||||||
|
buf, err := os.ReadFile("/etc/os-release")
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("Failed to read /etc/os-release: %v", err)
|
||||||
|
}
|
||||||
|
lines := strings.Split(string(buf), "\n")
|
||||||
|
for _, l := range lines {
|
||||||
|
if strings.HasPrefix(l, "PRETTY_NAME=") {
|
||||||
|
words := strings.Split(l, "\"")
|
||||||
|
if len(words) >= 2 {
|
||||||
|
return words[1], nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "unknown", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCapabilities gets the Linux capabilities (e.g. setuid, setgid). See https://docs.docker.com/engine/reference/run/#runtime-privilege-and-linux-capabilities
|
||||||
|
func GetContainerCapabilities() (map[string][]string, error) {
|
||||||
|
//passing the pid as 1 since runmqserver initializes, creates and starts a queue manager, as PID 1 in a container
|
||||||
|
return GetCapabilities(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSeccomp gets the seccomp enforcing mode, which affects which kernel calls can be made
|
||||||
|
func GetSeccomp() SeccompMode {
|
||||||
|
//passing the pid as 1 since runmqserver initializes, creates and starts a queue manager, as PID 1 in a container
|
||||||
|
return GetSeccompEnforcingMode(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSecurityAttributes gets the security attributes of the current process.
|
||||||
|
// The security attributes indicate whether AppArmor or SELinux are being used,
|
||||||
|
// and what the level of confinement is.
|
||||||
|
func GetSecurityAttributes() string {
|
||||||
|
a, err := readProc("/proc/self/attr/current")
|
||||||
|
// On some systems, if AppArmor or SELinux are not installed, you get an
|
||||||
|
// error when you try and read `/proc/self/attr/current`, even though the
|
||||||
|
// file exists.
|
||||||
|
if err != nil || a == "" {
|
||||||
|
a = "none"
|
||||||
|
}
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
func readProc(filename string) (value string, err error) {
|
||||||
|
// #nosec G304
|
||||||
|
buf, err := os.ReadFile(filename)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return strings.TrimSpace(string(buf)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetMounts() (map[string]string, error) {
|
||||||
|
all, err := readProc("/proc/mounts")
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Couldn't read /proc/mounts")
|
||||||
|
}
|
||||||
|
result := make(map[string]string)
|
||||||
|
lines := strings.Split(all, "\n")
|
||||||
|
for i := range lines {
|
||||||
|
parts := strings.Split(lines[i], " ")
|
||||||
|
//dev := parts[0]
|
||||||
|
mountPoint := parts[1]
|
||||||
|
fsType := parts[2]
|
||||||
|
if strings.Contains(mountPoint, "/mnt/mqm") {
|
||||||
|
result[mountPoint] = fsType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetKernelVersion() (string, error) {
|
||||||
|
return readProc("/proc/sys/kernel/osrelease")
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetMaxFileHandles() (string, error) {
|
||||||
|
return readProc("/proc/sys/fs/file-max")
|
||||||
|
}
|
||||||
|
|
||||||
|
// SupportedFilesystem returns true if the supplied filesystem type is supported for MQ data
|
||||||
|
func SupportedFilesystem(fsType string) bool {
|
||||||
|
switch fsType {
|
||||||
|
case "aufs", "overlayfs", "tmpfs":
|
||||||
|
return false
|
||||||
|
default:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidMultiInstanceFilesystem returns true if the supplied filesystem type is valid for a multi-instance queue manager
|
||||||
|
func ValidMultiInstanceFilesystem(fsType string) bool {
|
||||||
|
if !SupportedFilesystem(fsType) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// TODO : check for non-shared filesystems & shared filesystems which are known not to work
|
||||||
|
return true
|
||||||
|
}
|
||||||
115
internal/containerruntime/runtime_linux.go
Normal file
115
internal/containerruntime/runtime_linux.go
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
// +build linux
|
||||||
|
|
||||||
|
/*
|
||||||
|
© Copyright IBM Corporation 2017, 2019
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
package containerruntime
|
||||||
|
|
||||||
|
import (
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
// fsTypes contains file system identifier codes.
|
||||||
|
// This code will not compile on some operating systems - Linux only.
|
||||||
|
var fsTypes = map[int64]string{
|
||||||
|
0x61756673: "aufs",
|
||||||
|
0xef53: "ext",
|
||||||
|
0x6969: "nfs",
|
||||||
|
0x65735546: "fuse",
|
||||||
|
0x9123683e: "btrfs",
|
||||||
|
0x01021994: "tmpfs",
|
||||||
|
0x794c7630: "overlayfs",
|
||||||
|
0x58465342: "xfs",
|
||||||
|
// less popular codes
|
||||||
|
0xadf5: "adfs",
|
||||||
|
0xadff: "affs",
|
||||||
|
0x5346414F: "afs",
|
||||||
|
0x0187: "autofs",
|
||||||
|
0x73757245: "coda",
|
||||||
|
0x28cd3d45: "cramfs",
|
||||||
|
0x453dcd28: "cramfs",
|
||||||
|
0x64626720: "debugfs",
|
||||||
|
0x73636673: "securityfs",
|
||||||
|
0xf97cff8c: "selinux",
|
||||||
|
0x43415d53: "smack",
|
||||||
|
0x858458f6: "ramfs",
|
||||||
|
0x958458f6: "hugetlbfs",
|
||||||
|
0x73717368: "squashfs",
|
||||||
|
0xf15f: "ecryptfs",
|
||||||
|
0x414A53: "efs",
|
||||||
|
0xabba1974: "xenfs",
|
||||||
|
0x3434: "nilfs",
|
||||||
|
0xF2F52010: "f2fs",
|
||||||
|
0xf995e849: "hpfs",
|
||||||
|
0x9660: "isofs",
|
||||||
|
0x72b6: "jffs2",
|
||||||
|
0x6165676C: "pstorefs",
|
||||||
|
0xde5e81e4: "efivarfs",
|
||||||
|
0x00c0ffee: "hostfs",
|
||||||
|
0x137F: "minix_14", // minix v1 fs, 14 char names
|
||||||
|
0x138F: "minix_30", // minix v1 fs, 30 char names
|
||||||
|
0x2468: "minix2_14", // minix v2 fs, 14 char names
|
||||||
|
0x2478: "minix2_30", // minix v2 fs, 30 char names
|
||||||
|
0x4d5a: "minix3_60", // minix v3 fs, 60 char names
|
||||||
|
0x4d44: "msdos",
|
||||||
|
0x564c: "ncp",
|
||||||
|
0x7461636f: "ocfs2",
|
||||||
|
0x9fa1: "openprom",
|
||||||
|
0x002f: "qnx4",
|
||||||
|
0x68191122: "qnx6",
|
||||||
|
0x6B414653: "afs_fs",
|
||||||
|
0x52654973: "reiserfs",
|
||||||
|
0x517B: "smb",
|
||||||
|
0x27e0eb: "cgroup",
|
||||||
|
0x63677270: "cgroup2",
|
||||||
|
0x7655821: "rdtgroup",
|
||||||
|
0x57AC6E9D: "stack_end",
|
||||||
|
0x74726163: "tracefs",
|
||||||
|
0x01021997: "v9fs",
|
||||||
|
0x62646576: "bdevfs",
|
||||||
|
0x64646178: "daxfs",
|
||||||
|
0x42494e4d: "binfmtfs",
|
||||||
|
0x1cd1: "devpts",
|
||||||
|
0xBAD1DEA: "futexfs",
|
||||||
|
0x50495045: "pipefs",
|
||||||
|
0x9fa0: "proc",
|
||||||
|
0x534F434B: "sockfs",
|
||||||
|
0x62656572: "sysfs",
|
||||||
|
0x9fa2: "usbdevice",
|
||||||
|
0x11307854: "mtd_inode",
|
||||||
|
0x09041934: "anon_inode",
|
||||||
|
0x73727279: "btrfs",
|
||||||
|
0x6e736673: "nsfs",
|
||||||
|
0xcafe4a11: "bpf",
|
||||||
|
0x5a3c69f0: "aafs",
|
||||||
|
0x15013346: "udf",
|
||||||
|
0x13661366: "balloon_kvm",
|
||||||
|
0x58295829: "zsmalloc",
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFilesystem returns the filesystem type for the specified path
|
||||||
|
func GetFilesystem(path string) (string, error) {
|
||||||
|
statfs := &unix.Statfs_t{}
|
||||||
|
err := unix.Statfs(path, statfs)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
// Use a type conversion to make type an int64. On s390x it's a uint32.
|
||||||
|
t, ok := fsTypes[int64(statfs.Type)]
|
||||||
|
if !ok {
|
||||||
|
return "unknown", nil
|
||||||
|
}
|
||||||
|
return t, nil
|
||||||
|
}
|
||||||
24
internal/containerruntime/runtime_other.go
Normal file
24
internal/containerruntime/runtime_other.go
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
// +build !linux
|
||||||
|
|
||||||
|
/*
|
||||||
|
© Copyright IBM Corporation 2018, 2019
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
package containerruntime
|
||||||
|
|
||||||
|
// Dummy version of this function, only for non-Linux systems.
|
||||||
|
// Having this allows unit tests to be run on other platforms (e.g. macOS)
|
||||||
|
func checkFS(path string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
61
internal/copy/copy.go
Normal file
61
internal/copy/copy.go
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
© Copyright IBM Corporation 2019
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package copy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/ibm-messaging/mq-container/internal/filecheck"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CopyFileMode(src, dest string, perm os.FileMode) error {
|
||||||
|
|
||||||
|
err := filecheck.CheckFileSource(src)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to open %s for copy: %v", src, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// #nosec G304 - filename variable 'src' is checked above to ensure it is valid
|
||||||
|
in, err := os.Open(src)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to open %s for copy: %v", src, err)
|
||||||
|
}
|
||||||
|
// #nosec G307 - local to this function, pose no harm.
|
||||||
|
defer in.Close()
|
||||||
|
|
||||||
|
// #nosec G304 - this func creates based on the input filemode.
|
||||||
|
out, err := os.OpenFile(dest, os.O_CREATE|os.O_WRONLY, perm)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to open %s for copy: %v", dest, err)
|
||||||
|
}
|
||||||
|
// #nosec G307 - local to this function, pose no harm.
|
||||||
|
defer out.Close()
|
||||||
|
|
||||||
|
_, err = io.Copy(out, in)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = out.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// CopyFile copies the specified file
|
||||||
|
func CopyFile(src, dest string) error {
|
||||||
|
return CopyFileMode(src, dest, 0770)
|
||||||
|
}
|
||||||
39
internal/filecheck/filecheck.go
Normal file
39
internal/filecheck/filecheck.go
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
© Copyright IBM Corporation 2019, 2023
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package filecheck
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/ibm-messaging/mq-container/internal/pathutils"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CheckFileSource checks the filename is valid
|
||||||
|
func CheckFileSource(fileName string) error {
|
||||||
|
|
||||||
|
absFile, _ := filepath.Abs(fileName)
|
||||||
|
|
||||||
|
prefixes := []string{"bin", "boot", "dev", "lib", "lib64", "proc", "sbin", "sys"}
|
||||||
|
for _, prefix := range prefixes {
|
||||||
|
if strings.HasPrefix(absFile, pathutils.CleanPath("/", prefix)) {
|
||||||
|
return fmt.Errorf("Filename resolves to invalid path '%v'", absFile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
40
internal/filecheck/filecheck_test.go
Normal file
40
internal/filecheck/filecheck_test.go
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
© Copyright IBM Corporation 2019
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package filecheck
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCheckFileSource(t *testing.T) {
|
||||||
|
|
||||||
|
invalidFilenames := []string{"/bin", "/boot", "/dev", "/lib", "/lib64", "/proc", "/sbin", "/sys", "/bin/myfile", "/boot/mydir/myfile", "/var/../dev", "/var/../lib/myfile"}
|
||||||
|
for _, invalidFilename := range invalidFilenames {
|
||||||
|
err := CheckFileSource(invalidFilename)
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("Expected to receive an error for filename '%v'", invalidFilename)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
validFilenames := []string{"/var", "/mydir/dev", "/mydir/dev/myfile"}
|
||||||
|
for _, validFilename := range validFilenames {
|
||||||
|
err := CheckFileSource(validFilename)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Unexpected error received: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
96
internal/fips/fips.go
Normal file
96
internal/fips/fips.go
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
/*
|
||||||
|
© Copyright IBM Corporation 2023
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
package fips
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/ibm-messaging/mq-container/internal/command"
|
||||||
|
"github.com/ibm-messaging/mq-container/pkg/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
FIPSEnabledType int
|
||||||
|
)
|
||||||
|
|
||||||
|
// FIPS has been turned off either because OS is not FIPS enabled or
|
||||||
|
// MQ_ENABLE_FIPS environment variable is set to "false"
|
||||||
|
const FIPS_ENABLED_OFF = 0
|
||||||
|
|
||||||
|
// FIPS is turned ON
|
||||||
|
const FIPS_ENABLED_ON = 1
|
||||||
|
|
||||||
|
// FIPS enabled at operating system level
|
||||||
|
const FIPS_ENABLED_PLATFORM = 1
|
||||||
|
|
||||||
|
// FIPS enabled via environment variable
|
||||||
|
const FIPS_ENABLED_ENV_VAR = 2
|
||||||
|
|
||||||
|
// Get FIPS enabled type.
|
||||||
|
func ProcessFIPSType(logs *logger.Logger) {
|
||||||
|
// Run "sysctl crypto.fips_enabled" command to determine if FIPS has been enabled
|
||||||
|
// on OS.
|
||||||
|
FIPSEnabledType = FIPS_ENABLED_OFF
|
||||||
|
|
||||||
|
out, _, err := command.Run("sysctl", "crypto.fips_enabled")
|
||||||
|
if err == nil {
|
||||||
|
// Check the output of the command for expected output
|
||||||
|
if strings.Contains(out, "crypto.fips_enabled = 1") {
|
||||||
|
FIPSEnabledType = FIPS_ENABLED_PLATFORM
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we have been asked to override FIPS cryptography
|
||||||
|
fipsOverride, fipsOverrideSet := os.LookupEnv("MQ_ENABLE_FIPS")
|
||||||
|
if fipsOverrideSet {
|
||||||
|
if strings.EqualFold(fipsOverride, "false") || strings.EqualFold(fipsOverride, "0") {
|
||||||
|
FIPSEnabledType = FIPS_ENABLED_OFF
|
||||||
|
} else if strings.EqualFold(fipsOverride, "true") || strings.EqualFold(fipsOverride, "1") {
|
||||||
|
// This is the case where OS may or may not be FIPS compliant but we have been asked
|
||||||
|
// to run MQ queue manager, web server and Native HA in FIPS mode. This case can also
|
||||||
|
// be used when running docker tests. If FIPS is enabled on host, then don't modify
|
||||||
|
// the original value.
|
||||||
|
if FIPSEnabledType != FIPS_ENABLED_PLATFORM {
|
||||||
|
FIPSEnabledType = FIPS_ENABLED_ENV_VAR
|
||||||
|
}
|
||||||
|
} else if strings.EqualFold(fipsOverride, "auto") {
|
||||||
|
// This is the default case. Leave it to the OS default as determined above.
|
||||||
|
} else {
|
||||||
|
// We don't recognise the value specified. Log a warning and carry on.
|
||||||
|
if logs != nil {
|
||||||
|
logs.Printf("Invalid value '%s' was specified for MQ_ENABLE_FIPS. The value has been ignored.\n", fipsOverride)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsFIPSEnabled() bool {
|
||||||
|
return FIPSEnabledType > FIPS_ENABLED_OFF
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log a message on the console to indicate FIPS certified
|
||||||
|
// cryptography being used.
|
||||||
|
func PostInit(log *logger.Logger) {
|
||||||
|
message := "FIPS cryptography is not enabled."
|
||||||
|
if FIPSEnabledType == FIPS_ENABLED_PLATFORM {
|
||||||
|
message = "FIPS cryptography is enabled. FIPS cryptography setting on the host is 'true'."
|
||||||
|
} else if FIPSEnabledType == FIPS_ENABLED_ENV_VAR {
|
||||||
|
message = "FIPS cryptography is enabled. FIPS cryptography setting on the host is 'false'."
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println(message)
|
||||||
|
}
|
||||||
65
internal/fips/fips_test.go
Normal file
65
internal/fips/fips_test.go
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
© Copyright IBM Corporation 2022
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Package keystore contains code to create and update keystores
|
||||||
|
package fips
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestEnableFIPSAuto(t *testing.T) {
|
||||||
|
ProcessFIPSType(nil)
|
||||||
|
// Test default "auto"
|
||||||
|
fipsType := IsFIPSEnabled()
|
||||||
|
if fipsType {
|
||||||
|
t.Errorf("Expected FIPS OFF but got %v\n", fipsType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEnableFIPSTrue(t *testing.T) {
|
||||||
|
// Test MQ_ENABLE_FIPS=true
|
||||||
|
os.Setenv("MQ_ENABLE_FIPS", "true")
|
||||||
|
fmt.Println(os.Getenv("MQ_ENABLE_FIPS"))
|
||||||
|
ProcessFIPSType(nil)
|
||||||
|
fipsType := IsFIPSEnabled()
|
||||||
|
if !fipsType {
|
||||||
|
t.Errorf("Expected FIPS ON but got %v\n", fipsType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEnableFIPSFalse(t *testing.T) {
|
||||||
|
// Test MQ_ENABLE_FIPS=false
|
||||||
|
os.Setenv("MQ_ENABLE_FIPS", "false")
|
||||||
|
ProcessFIPSType(nil)
|
||||||
|
fipsType := IsFIPSEnabled()
|
||||||
|
if fipsType {
|
||||||
|
t.Errorf("Expected FIPS OFF but got %v\n", fipsType)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEnableFIPSInvalid(t *testing.T) {
|
||||||
|
// Test MQ_ENABLE_FIPS with invalid value
|
||||||
|
os.Setenv("MQ_ENABLE_FIPS", "falseOff")
|
||||||
|
ProcessFIPSType(nil)
|
||||||
|
fipsType := IsFIPSEnabled()
|
||||||
|
if fipsType {
|
||||||
|
t.Errorf("Expected FIPS OFF but got %v\n", fipsType)
|
||||||
|
}
|
||||||
|
}
|
||||||
215
internal/ha/ha.go
Normal file
215
internal/ha/ha.go
Normal file
@@ -0,0 +1,215 @@
|
|||||||
|
/*
|
||||||
|
© Copyright IBM Corporation 2020, 2024
|
||||||
|
|
||||||
|
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 (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/ibm-messaging/mq-container/internal/fips"
|
||||||
|
"github.com/ibm-messaging/mq-container/internal/mqtemplate"
|
||||||
|
"github.com/ibm-messaging/mq-container/internal/tls"
|
||||||
|
"github.com/ibm-messaging/mq-container/pkg/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ConfigureNativeHA configures native high availability
|
||||||
|
func ConfigureNativeHA(log *logger.Logger) error {
|
||||||
|
if os.Getenv("MQ_NATIVE_HA") != "true" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
fipsAvailable := fips.IsFIPSEnabled()
|
||||||
|
|
||||||
|
haCertLabel, haGroupCertLabel, _, _, err := tls.ConfigureHATLSKeystore()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error loading tls keys: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
configFiles := map[string]string{
|
||||||
|
"/run/10-native-ha-instance.ini": "/etc/mqm/10-native-ha-instance.ini.tpl",
|
||||||
|
}
|
||||||
|
if haCertLabel != "" || haGroupCertLabel != "" {
|
||||||
|
configFiles["/run/10-native-ha-keystore.ini"] = "/etc/mqm/10-native-ha-keystore.ini.tpl"
|
||||||
|
}
|
||||||
|
if envConfigPresent() {
|
||||||
|
log.Println("Configuring Native HA using values provided in environment variables")
|
||||||
|
configFiles["/run/10-native-ha.ini"] = "/etc/mqm/10-native-ha.ini.tpl"
|
||||||
|
}
|
||||||
|
return loadConfigAndGenerate(configFiles, fipsAvailable, haCertLabel, haGroupCertLabel, log)
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadConfigAndGenerate(templateConfigs map[string]string, fipsAvailable bool, haCertLabel, haGroupCertLabel string, log *logger.Logger) error {
|
||||||
|
cfg, err := loadConfigFromEnv(log)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = cfg.updateTLS(fipsAvailable, haCertLabel, haGroupCertLabel)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for outputPath, templateFile := range templateConfigs {
|
||||||
|
err := cfg.generate(templateFile, outputPath, log)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func envConfigPresent() bool {
|
||||||
|
checkVars := []string{
|
||||||
|
"MQ_NATIVE_HA_INSTANCE_0_NAME",
|
||||||
|
"MQ_NATIVE_HA_INSTANCE_0_REPLICATION_ADDRESS",
|
||||||
|
"MQ_NATIVE_HA_INSTANCE_1_NAME",
|
||||||
|
"MQ_NATIVE_HA_INSTANCE_1_REPLICATION_ADDRESS",
|
||||||
|
"MQ_NATIVE_HA_INSTANCE_2_NAME",
|
||||||
|
"MQ_NATIVE_HA_INSTANCE_2_REPLICATION_ADDRESS",
|
||||||
|
"MQ_NATIVE_HA_TLS",
|
||||||
|
"MQ_NATIVE_HA_CIPHERSPEC",
|
||||||
|
}
|
||||||
|
for _, checkVar := range checkVars {
|
||||||
|
if os.Getenv(checkVar) != "" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadConfigFromEnv(log *logger.Logger) (*haConfig, error) {
|
||||||
|
cfg := &haConfig{
|
||||||
|
Name: os.Getenv("HOSTNAME"),
|
||||||
|
Instances: [3]haInstance{
|
||||||
|
{
|
||||||
|
Name: os.Getenv("MQ_NATIVE_HA_INSTANCE_0_NAME"),
|
||||||
|
ReplicationAddress: os.Getenv("MQ_NATIVE_HA_INSTANCE_0_REPLICATION_ADDRESS"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: os.Getenv("MQ_NATIVE_HA_INSTANCE_1_NAME"),
|
||||||
|
ReplicationAddress: os.Getenv("MQ_NATIVE_HA_INSTANCE_1_REPLICATION_ADDRESS"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: os.Getenv("MQ_NATIVE_HA_INSTANCE_2_NAME"),
|
||||||
|
ReplicationAddress: os.Getenv("MQ_NATIVE_HA_INSTANCE_2_REPLICATION_ADDRESS"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Group: haGroupConfig{
|
||||||
|
Local: haLocalGroupConfig{
|
||||||
|
Address: os.Getenv("MQ_NATIVE_HA_GROUP_LOCAL_ADDRESS"),
|
||||||
|
Name: os.Getenv("MQ_NATIVE_HA_GROUP_LOCAL_NAME"),
|
||||||
|
Role: os.Getenv("MQ_NATIVE_HA_GROUP_ROLE"),
|
||||||
|
},
|
||||||
|
Recovery: haRecoveryGroupConfig{
|
||||||
|
Address: os.Getenv("MQ_NATIVE_HA_GROUP_REPLICATION_ADDRESS"),
|
||||||
|
Name: os.Getenv("MQ_NATIVE_HA_GROUP_RECOVERY_NAME"),
|
||||||
|
Enabled: os.Getenv("MQ_NATIVE_HA_GROUP_RECOVERY_ENABLED") != "false",
|
||||||
|
},
|
||||||
|
CipherSpec: os.Getenv("MQ_NATIVE_HA_GROUP_CIPHERSPEC"),
|
||||||
|
},
|
||||||
|
CipherSpec: os.Getenv("MQ_NATIVE_HA_CIPHERSPEC"),
|
||||||
|
keyRepository: os.Getenv("MQ_NATIVE_HA_KEY_REPOSITORY"),
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.Group.Recovery.Name == "" {
|
||||||
|
cfg.Group.Recovery.Enabled = false
|
||||||
|
}
|
||||||
|
|
||||||
|
return cfg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type haConfig struct {
|
||||||
|
Name string
|
||||||
|
Instances [3]haInstance
|
||||||
|
Group haGroupConfig
|
||||||
|
|
||||||
|
haTLSEnabled bool
|
||||||
|
CipherSpec string
|
||||||
|
CertificateLabel string
|
||||||
|
keyRepository string
|
||||||
|
fipsAvailable bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h haConfig) ShouldConfigureTLS() bool {
|
||||||
|
if h.haTLSEnabled {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if h.Group.Local.Name != "" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h haConfig) SSLFipsRequired() string {
|
||||||
|
return yesNo(h.fipsAvailable).String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *haConfig) updateTLS(fipsAvailable bool, haCertLabel, haGroupCertLabel string) error {
|
||||||
|
if haCertLabel != "" {
|
||||||
|
h.CertificateLabel = haCertLabel
|
||||||
|
h.haTLSEnabled = true
|
||||||
|
}
|
||||||
|
if haGroupCertLabel != "" {
|
||||||
|
h.Group.CertificateLabel = haGroupCertLabel
|
||||||
|
h.haTLSEnabled = true
|
||||||
|
}
|
||||||
|
|
||||||
|
h.fipsAvailable = fipsAvailable
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h haConfig) generate(templatePath string, outputPath string, log *logger.Logger) error {
|
||||||
|
return mqtemplate.ProcessTemplateFile(templatePath, outputPath, h, log)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h haConfig) KeyRepository() string {
|
||||||
|
if h.keyRepository != "" {
|
||||||
|
return h.keyRepository
|
||||||
|
}
|
||||||
|
return "/run/runmqserver/ha/tls/key"
|
||||||
|
}
|
||||||
|
|
||||||
|
type haGroupConfig struct {
|
||||||
|
Local haLocalGroupConfig
|
||||||
|
Recovery haRecoveryGroupConfig
|
||||||
|
CipherSpec string
|
||||||
|
CertificateLabel string
|
||||||
|
}
|
||||||
|
|
||||||
|
type haLocalGroupConfig struct {
|
||||||
|
Name string
|
||||||
|
Role string
|
||||||
|
Address string
|
||||||
|
}
|
||||||
|
type haRecoveryGroupConfig struct {
|
||||||
|
Name string
|
||||||
|
Enabled yesNo
|
||||||
|
Address string
|
||||||
|
}
|
||||||
|
|
||||||
|
type haInstance struct {
|
||||||
|
Name string
|
||||||
|
ReplicationAddress string
|
||||||
|
}
|
||||||
|
|
||||||
|
type yesNo bool
|
||||||
|
|
||||||
|
func (yn yesNo) String() string {
|
||||||
|
if yn {
|
||||||
|
return "Yes"
|
||||||
|
}
|
||||||
|
return "No"
|
||||||
|
}
|
||||||
545
internal/ha/ha_test.go
Normal file
545
internal/ha/ha_test.go
Normal file
@@ -0,0 +1,545 @@
|
|||||||
|
/*
|
||||||
|
© Copyright IBM Corporation 2024
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"embed"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ibm-messaging/mq-container/pkg/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed test_fixtures
|
||||||
|
var testFixtures embed.FS
|
||||||
|
|
||||||
|
func TestConfigFromEnv(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
TestName string
|
||||||
|
env map[string]string
|
||||||
|
overrides testOverrides
|
||||||
|
expected haConfig
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
TestName: "Minimal config",
|
||||||
|
env: map[string]string{
|
||||||
|
"HOSTNAME": "minimal-config",
|
||||||
|
"MQ_NATIVE_HA_INSTANCE_0_NAME": "minimal-config-instance0",
|
||||||
|
"MQ_NATIVE_HA_INSTANCE_1_NAME": "minimal-config-instance1",
|
||||||
|
"MQ_NATIVE_HA_INSTANCE_2_NAME": "minimal-config-instance2",
|
||||||
|
"MQ_NATIVE_HA_INSTANCE_0_REPLICATION_ADDRESS": "minimal-config-instance0(9145)",
|
||||||
|
"MQ_NATIVE_HA_INSTANCE_1_REPLICATION_ADDRESS": "minimal-config-instance1(9145)",
|
||||||
|
"MQ_NATIVE_HA_INSTANCE_2_REPLICATION_ADDRESS": "minimal-config-instance2(9145)",
|
||||||
|
},
|
||||||
|
expected: haConfig{
|
||||||
|
Name: "minimal-config",
|
||||||
|
Instances: [3]haInstance{
|
||||||
|
{"minimal-config-instance0", "minimal-config-instance0(9145)"},
|
||||||
|
{"minimal-config-instance1", "minimal-config-instance1(9145)"},
|
||||||
|
{"minimal-config-instance2", "minimal-config-instance2(9145)"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
TestName: "Full TLS config",
|
||||||
|
env: map[string]string{
|
||||||
|
"HOSTNAME": "tls-config",
|
||||||
|
"MQ_NATIVE_HA_INSTANCE_0_NAME": "tls-config-instance0",
|
||||||
|
"MQ_NATIVE_HA_INSTANCE_1_NAME": "tls-config-instance1",
|
||||||
|
"MQ_NATIVE_HA_INSTANCE_2_NAME": "tls-config-instance2",
|
||||||
|
"MQ_NATIVE_HA_INSTANCE_0_REPLICATION_ADDRESS": "tls-config-instance0(9145)",
|
||||||
|
"MQ_NATIVE_HA_INSTANCE_1_REPLICATION_ADDRESS": "tls-config-instance1(9145)",
|
||||||
|
"MQ_NATIVE_HA_INSTANCE_2_REPLICATION_ADDRESS": "tls-config-instance2(9145)",
|
||||||
|
"MQ_NATIVE_HA_TLS": "true",
|
||||||
|
"MQ_NATIVE_HA_CIPHERSPEC": "a-cipher-spec",
|
||||||
|
"MQ_NATIVE_HA_KEY_REPOSITORY": "/path/to/repository",
|
||||||
|
},
|
||||||
|
overrides: testOverrides{
|
||||||
|
certificateLabel: asRef("cert-label-here"),
|
||||||
|
fips: asRef(false),
|
||||||
|
},
|
||||||
|
expected: haConfig{
|
||||||
|
Name: "tls-config",
|
||||||
|
Instances: [3]haInstance{
|
||||||
|
{"tls-config-instance0", "tls-config-instance0(9145)"},
|
||||||
|
{"tls-config-instance1", "tls-config-instance1(9145)"},
|
||||||
|
{"tls-config-instance2", "tls-config-instance2(9145)"},
|
||||||
|
},
|
||||||
|
haTLSEnabled: true,
|
||||||
|
CipherSpec: "a-cipher-spec",
|
||||||
|
keyRepository: "/path/to/repository",
|
||||||
|
|
||||||
|
CertificateLabel: "cert-label-here", // From override
|
||||||
|
fipsAvailable: false, // From override
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
TestName: "Group TLS (live plain) config",
|
||||||
|
env: map[string]string{
|
||||||
|
"HOSTNAME": "group-live-plain-config",
|
||||||
|
"MQ_NATIVE_HA_INSTANCE_0_NAME": "group-live-plain-config0",
|
||||||
|
"MQ_NATIVE_HA_INSTANCE_1_NAME": "group-live-plain-config1",
|
||||||
|
"MQ_NATIVE_HA_INSTANCE_2_NAME": "group-live-plain-config2",
|
||||||
|
"MQ_NATIVE_HA_INSTANCE_0_REPLICATION_ADDRESS": "group-live-plain-config0(9145)",
|
||||||
|
"MQ_NATIVE_HA_INSTANCE_1_REPLICATION_ADDRESS": "group-live-plain-config1(9145)",
|
||||||
|
"MQ_NATIVE_HA_INSTANCE_2_REPLICATION_ADDRESS": "group-live-plain-config2(9145)",
|
||||||
|
"MQ_NATIVE_HA_CIPHERSPEC": "NULL",
|
||||||
|
"MQ_NATIVE_HA_KEY_REPOSITORY": "/path/to/repository",
|
||||||
|
|
||||||
|
"MQ_NATIVE_HA_GROUP_RECOVERY_ENABLED": "true",
|
||||||
|
"MQ_NATIVE_HA_GROUP_LOCAL_NAME": "alpha",
|
||||||
|
"MQ_NATIVE_HA_GROUP_RECOVERY_NAME": "beta",
|
||||||
|
"MQ_NATIVE_HA_GROUP_CIPHERSPEC": "ANY_TLS",
|
||||||
|
"MQ_NATIVE_HA_GROUP_ROLE": "Live",
|
||||||
|
"MQ_NATIVE_HA_GROUP_LOCAL_ADDRESS": "(4445)",
|
||||||
|
"MQ_NATIVE_HA_GROUP_REPLICATION_ADDRESS": "beta-address(4445)",
|
||||||
|
},
|
||||||
|
overrides: testOverrides{
|
||||||
|
groupCertificateLabel: asRef("recovery-cert-label-here"),
|
||||||
|
fips: asRef(false),
|
||||||
|
},
|
||||||
|
expected: haConfig{
|
||||||
|
Name: "group-live-plain-config",
|
||||||
|
Instances: [3]haInstance{
|
||||||
|
{"group-live-plain-config0", "group-live-plain-config0(9145)"},
|
||||||
|
{"group-live-plain-config1", "group-live-plain-config1(9145)"},
|
||||||
|
{"group-live-plain-config2", "group-live-plain-config2(9145)"},
|
||||||
|
},
|
||||||
|
Group: haGroupConfig{
|
||||||
|
Local: haLocalGroupConfig{
|
||||||
|
Name: "alpha",
|
||||||
|
Role: "Live",
|
||||||
|
Address: "(4445)",
|
||||||
|
},
|
||||||
|
Recovery: haRecoveryGroupConfig{
|
||||||
|
Name: "beta",
|
||||||
|
Enabled: true,
|
||||||
|
Address: "beta-address(4445)",
|
||||||
|
},
|
||||||
|
CertificateLabel: "recovery-cert-label-here", // From override
|
||||||
|
CipherSpec: "ANY_TLS",
|
||||||
|
},
|
||||||
|
CipherSpec: "NULL",
|
||||||
|
keyRepository: "/path/to/repository",
|
||||||
|
haTLSEnabled: true,
|
||||||
|
|
||||||
|
fipsAvailable: false, // From override
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.TestName, func(t *testing.T) {
|
||||||
|
// Set environment for test
|
||||||
|
savedEnv := make([]string, len(os.Environ()))
|
||||||
|
copy(savedEnv, os.Environ())
|
||||||
|
defer func() {
|
||||||
|
os.Clearenv()
|
||||||
|
for _, env := range savedEnv {
|
||||||
|
parts := strings.SplitN(env, "=", 2)
|
||||||
|
os.Setenv(parts[0], parts[1])
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
for key, value := range test.env {
|
||||||
|
os.Setenv(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
testLogger, logBuffer, err := newTestLogger(test.TestName)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to create test logger: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if !envConfigPresent() {
|
||||||
|
t.Fatalf("Check for Native HA config by environment variable unexpectedly reported false")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load config from env
|
||||||
|
cfg, err := loadConfigFromEnv(testLogger)
|
||||||
|
t.Log(logBuffer.String())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Loading config failed: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
test.overrides.apply(cfg)
|
||||||
|
|
||||||
|
// Validate
|
||||||
|
if *cfg != test.expected {
|
||||||
|
t.Fatalf("Configuration does not match expected:\n\tExpected: %#v\n\tActual: %#v\n", test.expected, *cfg)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCheckEnv(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
env map[string]string
|
||||||
|
expect bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "empty env",
|
||||||
|
expect: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Native HA with external config",
|
||||||
|
env: map[string]string{
|
||||||
|
"HOSTNAME": "external-config",
|
||||||
|
"MQ_NATIVE_HA": "true",
|
||||||
|
},
|
||||||
|
expect: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Native HA with env config",
|
||||||
|
env: map[string]string{
|
||||||
|
"MQ_NATIVE_HA": "true",
|
||||||
|
"MQ_NATIVE_HA_INSTANCE_0_NAME": "minimal-config-instance0",
|
||||||
|
"MQ_NATIVE_HA_INSTANCE_1_NAME": "minimal-config-instance1",
|
||||||
|
"MQ_NATIVE_HA_INSTANCE_2_NAME": "minimal-config-instance2",
|
||||||
|
"MQ_NATIVE_HA_INSTANCE_0_REPLICATION_ADDRESS": "minimal-config-instance0(9145)",
|
||||||
|
"MQ_NATIVE_HA_INSTANCE_1_REPLICATION_ADDRESS": "minimal-config-instance1(9145)",
|
||||||
|
"MQ_NATIVE_HA_INSTANCE_2_REPLICATION_ADDRESS": "minimal-config-instance2(9145)",
|
||||||
|
},
|
||||||
|
expect: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
// Set environment for test
|
||||||
|
savedEnv := make([]string, len(os.Environ()))
|
||||||
|
copy(savedEnv, os.Environ())
|
||||||
|
defer func() {
|
||||||
|
os.Clearenv()
|
||||||
|
for _, env := range savedEnv {
|
||||||
|
parts := strings.SplitN(env, "=", 2)
|
||||||
|
os.Setenv(parts[0], parts[1])
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
for key, value := range test.env {
|
||||||
|
os.Setenv(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
actual := envConfigPresent()
|
||||||
|
if actual != test.expect {
|
||||||
|
t.Fatalf("Incorrect result from environment variable check (actual: %v != expected: %v)", actual, test.expect)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTemplatingFromConfig(t *testing.T) {
|
||||||
|
// Helper function to turn pairs of strings into a map
|
||||||
|
templateListToMap := func(paths ...string) map[string]string {
|
||||||
|
templates := map[string]string{}
|
||||||
|
for i := 0; i+1 < len(paths); i += 2 {
|
||||||
|
input := path.Join("../../ha/", paths[i])
|
||||||
|
output := paths[i+1]
|
||||||
|
templates[input] = output
|
||||||
|
}
|
||||||
|
return templates
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
TestName string
|
||||||
|
config haConfig
|
||||||
|
templates map[string]string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
TestName: "MinimalConfig (no-FIPS)",
|
||||||
|
config: haConfig{
|
||||||
|
fipsAvailable: false,
|
||||||
|
},
|
||||||
|
templates: templateListToMap(
|
||||||
|
"10-native-ha-instance.ini.tpl", "instance/no-fips.ini",
|
||||||
|
"10-native-ha.ini.tpl", "envcfg/minimal-config.ini",
|
||||||
|
),
|
||||||
|
}, {
|
||||||
|
TestName: "MinimalConfig (FIPS)",
|
||||||
|
config: haConfig{
|
||||||
|
fipsAvailable: true,
|
||||||
|
},
|
||||||
|
templates: templateListToMap(
|
||||||
|
"10-native-ha-instance.ini.tpl", "instance/fips.ini",
|
||||||
|
"10-native-ha.ini.tpl", "envcfg/minimal-config.ini",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
TestName: "Base TLS config (no FIPS)",
|
||||||
|
config: haConfig{
|
||||||
|
haTLSEnabled: true,
|
||||||
|
CertificateLabel: "baseTLS",
|
||||||
|
fipsAvailable: false,
|
||||||
|
},
|
||||||
|
templates: templateListToMap(
|
||||||
|
"10-native-ha.ini.tpl", "envcfg/minimal-config.ini",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
TestName: "Base TLS config (with FIPS)",
|
||||||
|
config: haConfig{
|
||||||
|
haTLSEnabled: true,
|
||||||
|
CertificateLabel: "baseTLS",
|
||||||
|
fipsAvailable: true,
|
||||||
|
},
|
||||||
|
templates: templateListToMap(
|
||||||
|
"10-native-ha-keystore.ini.tpl", "keystore/ha-only.ini",
|
||||||
|
"10-native-ha.ini.tpl", "envcfg/minimal-config.ini",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
TestName: "Full TLS config (no-fips)",
|
||||||
|
config: haConfig{
|
||||||
|
haTLSEnabled: true,
|
||||||
|
CipherSpec: "some-cipher",
|
||||||
|
keyRepository: "/an/overridden/keystore",
|
||||||
|
fipsAvailable: false,
|
||||||
|
},
|
||||||
|
templates: templateListToMap(
|
||||||
|
"10-native-ha-instance.ini.tpl", "instance/no-fips.ini",
|
||||||
|
"10-native-ha-keystore.ini.tpl", "keystore/overridden-path.ini",
|
||||||
|
"10-native-ha.ini.tpl", "envcfg/tls-full.ini",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
TestName: "Minimal live config",
|
||||||
|
config: haConfig{
|
||||||
|
Group: haGroupConfig{
|
||||||
|
Local: haLocalGroupConfig{
|
||||||
|
Name: "alpha",
|
||||||
|
Role: "Live",
|
||||||
|
},
|
||||||
|
Recovery: haRecoveryGroupConfig{
|
||||||
|
Name: "beta",
|
||||||
|
Enabled: true,
|
||||||
|
Address: "beta-address(4445)",
|
||||||
|
},
|
||||||
|
CertificateLabel: "recoveryTLS",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
templates: templateListToMap(
|
||||||
|
"10-native-ha-instance.ini.tpl", "instance/no-fips.ini",
|
||||||
|
"10-native-ha-keystore.ini.tpl", "keystore/group-only.ini",
|
||||||
|
"10-native-ha.ini.tpl", "envcfg/group-live-minimal.ini",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
TestName: "Minimal recovery config",
|
||||||
|
config: haConfig{
|
||||||
|
Group: haGroupConfig{
|
||||||
|
Local: haLocalGroupConfig{
|
||||||
|
Name: "beta",
|
||||||
|
Role: "Recovery",
|
||||||
|
},
|
||||||
|
Recovery: haRecoveryGroupConfig{
|
||||||
|
Name: "alpha",
|
||||||
|
Enabled: true,
|
||||||
|
Address: "alpha-address(4445)",
|
||||||
|
},
|
||||||
|
CertificateLabel: "recoveryTLS",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
templates: templateListToMap(
|
||||||
|
"10-native-ha-instance.ini.tpl", "instance/no-fips.ini",
|
||||||
|
"10-native-ha-keystore.ini.tpl", "keystore/group-only.ini",
|
||||||
|
"10-native-ha.ini.tpl", "envcfg/group-recovery-minimal.ini",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
TestName: "Group TLS (live plain) config",
|
||||||
|
config: haConfig{
|
||||||
|
Group: haGroupConfig{
|
||||||
|
Local: haLocalGroupConfig{
|
||||||
|
Name: "alpha",
|
||||||
|
Role: "Live",
|
||||||
|
Address: "(4445)",
|
||||||
|
},
|
||||||
|
Recovery: haRecoveryGroupConfig{
|
||||||
|
Name: "beta",
|
||||||
|
Enabled: true,
|
||||||
|
Address: "beta-address(4445)",
|
||||||
|
},
|
||||||
|
CertificateLabel: "recoveryTLS",
|
||||||
|
CipherSpec: "ANY_TLS",
|
||||||
|
},
|
||||||
|
CipherSpec: "NULL",
|
||||||
|
},
|
||||||
|
templates: templateListToMap(
|
||||||
|
"10-native-ha-instance.ini.tpl", "instance/no-fips.ini",
|
||||||
|
"10-native-ha-keystore.ini.tpl", "keystore/group-only.ini",
|
||||||
|
"10-native-ha.ini.tpl", "envcfg/group-live-plain-ha.ini",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
TestName: "Separate HA and Group TLS config",
|
||||||
|
config: haConfig{
|
||||||
|
Group: haGroupConfig{
|
||||||
|
Local: haLocalGroupConfig{
|
||||||
|
Name: "alpha",
|
||||||
|
Role: "Live",
|
||||||
|
Address: "(4445)",
|
||||||
|
},
|
||||||
|
Recovery: haRecoveryGroupConfig{
|
||||||
|
Name: "beta",
|
||||||
|
Enabled: true,
|
||||||
|
Address: "beta-address(4445)",
|
||||||
|
},
|
||||||
|
CertificateLabel: "recoveryTLS",
|
||||||
|
CipherSpec: "ANY_TLS",
|
||||||
|
},
|
||||||
|
CertificateLabel: "baseTLS",
|
||||||
|
CipherSpec: "NULL",
|
||||||
|
},
|
||||||
|
templates: templateListToMap(
|
||||||
|
"10-native-ha-instance.ini.tpl", "instance/no-fips.ini",
|
||||||
|
"10-native-ha-keystore.ini.tpl", "keystore/ha-group.ini",
|
||||||
|
"10-native-ha.ini.tpl", "envcfg/group-live-plain-ha.ini",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.TestName, func(t *testing.T) {
|
||||||
|
for templateFile, expectedFile := range test.templates {
|
||||||
|
t.Run(templateFile, func(t *testing.T) {
|
||||||
|
t.Logf(`Runing templating test "%s"`, test.TestName)
|
||||||
|
t.Logf(`Expected to match template "%s"`, expectedFile)
|
||||||
|
testLogger, logBuffer, err := newTestLogger(test.TestName)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to create test logger: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load test config
|
||||||
|
cfg := applyTestDefaults(test.config)
|
||||||
|
|
||||||
|
// Generate template
|
||||||
|
tempOutputPath, err := os.CreateTemp("", "")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to create temporary output file: %s", err.Error())
|
||||||
|
}
|
||||||
|
defer func() { _ = os.Remove(tempOutputPath.Name()) }()
|
||||||
|
err = cfg.generate(templateFile, tempOutputPath.Name(), testLogger)
|
||||||
|
t.Log(logBuffer.String())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Processing template to config failed: %s", err.Error())
|
||||||
|
}
|
||||||
|
actual, err := os.ReadFile(tempOutputPath.Name())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to read '%s': %s", test.TestName, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate
|
||||||
|
assertIniMatch(t, string(actual), expectedFile)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func applyTestDefaults(testConfig haConfig) haConfig {
|
||||||
|
baseName := "test-config"
|
||||||
|
setIfBlank(&testConfig.Name, baseName)
|
||||||
|
for i := 0; i < 3; i++ {
|
||||||
|
instName := fmt.Sprintf("%s-instance%d", baseName, i)
|
||||||
|
replAddress := fmt.Sprintf("%s(9145)", instName)
|
||||||
|
setIfBlank(&testConfig.Instances[i].Name, instName)
|
||||||
|
setIfBlank(&testConfig.Instances[i].ReplicationAddress, replAddress)
|
||||||
|
}
|
||||||
|
return testConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
func setIfBlank[T comparable](setting *T, val T) {
|
||||||
|
var zero T
|
||||||
|
if *setting == zero {
|
||||||
|
*setting = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func assertIniMatch(t *testing.T, actual string, expectedResultName string) {
|
||||||
|
expectedContent, err := testFixtures.ReadFile(fmt.Sprintf("test_fixtures/%s", expectedResultName))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to read expected results file (%s): %s", expectedResultName, err.Error())
|
||||||
|
}
|
||||||
|
expectedLines := strings.Split(string(expectedContent), "\n")
|
||||||
|
actualLines := strings.Split(actual, "\n")
|
||||||
|
|
||||||
|
filterBlank := func(lines *[]string) {
|
||||||
|
n := 0
|
||||||
|
for i := 0; i < len(*lines); i++ {
|
||||||
|
if strings.TrimSpace((*lines)[i]) == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
(*lines)[n] = (*lines)[i]
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
*lines = (*lines)[0:n]
|
||||||
|
}
|
||||||
|
filterBlank(&expectedLines)
|
||||||
|
filterBlank(&actualLines)
|
||||||
|
|
||||||
|
maxLine := len(expectedLines)
|
||||||
|
if len(actualLines) > maxLine {
|
||||||
|
maxLine = len(actualLines)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < maxLine; i++ {
|
||||||
|
actLine, expLine := "", ""
|
||||||
|
if i < len(actualLines) {
|
||||||
|
actLine = actualLines[i]
|
||||||
|
}
|
||||||
|
if i < len(expectedLines) {
|
||||||
|
expLine = expectedLines[i]
|
||||||
|
}
|
||||||
|
if actLine != expLine {
|
||||||
|
t.Fatalf("Template does not match\n\nFirst difference at line %d:\n\tExpected: %s\n\tActual : %s\n\nExpected:\n\t%s\n\nActual:\n\t%s", i+1, expLine, actLine, strings.Join(expectedLines, "\n\t"), strings.Join(actualLines, "\n\t"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTestLogger(name string) (*logger.Logger, *bytes.Buffer, error) {
|
||||||
|
buffer := new(bytes.Buffer)
|
||||||
|
l, err := logger.NewLogger(buffer, true, false, name)
|
||||||
|
return l, buffer, err
|
||||||
|
}
|
||||||
|
|
||||||
|
type testOverrides struct {
|
||||||
|
certificateLabel *string
|
||||||
|
groupCertificateLabel *string
|
||||||
|
fips *bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t testOverrides) apply(cfg *haConfig) {
|
||||||
|
if t.certificateLabel != nil {
|
||||||
|
cfg.CertificateLabel = *t.certificateLabel
|
||||||
|
cfg.haTLSEnabled = true
|
||||||
|
}
|
||||||
|
if t.groupCertificateLabel != nil {
|
||||||
|
cfg.Group.CertificateLabel = *t.groupCertificateLabel
|
||||||
|
cfg.haTLSEnabled = true
|
||||||
|
}
|
||||||
|
if t.fips != nil {
|
||||||
|
cfg.fipsAvailable = *t.fips
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func asRef[T any](val T) *T {
|
||||||
|
ref := &val
|
||||||
|
return ref
|
||||||
|
}
|
||||||
16
internal/ha/test_fixtures/envcfg/group-live-minimal.ini
Normal file
16
internal/ha/test_fixtures/envcfg/group-live-minimal.ini
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
NativeHALocalInstance:
|
||||||
|
GroupName=alpha
|
||||||
|
GroupRole=Live
|
||||||
|
NativeHAInstance:
|
||||||
|
Name=test-config-instance0
|
||||||
|
ReplicationAddress=test-config-instance0(9145)
|
||||||
|
NativeHAInstance:
|
||||||
|
Name=test-config-instance1
|
||||||
|
ReplicationAddress=test-config-instance1(9145)
|
||||||
|
NativeHAInstance:
|
||||||
|
Name=test-config-instance2
|
||||||
|
ReplicationAddress=test-config-instance2(9145)
|
||||||
|
NativeHARecoveryGroup:
|
||||||
|
GroupName=beta
|
||||||
|
Enabled=Yes
|
||||||
|
ReplicationAddress=beta-address(4445)
|
||||||
19
internal/ha/test_fixtures/envcfg/group-live-plain-ha.ini
Normal file
19
internal/ha/test_fixtures/envcfg/group-live-plain-ha.ini
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
NativeHALocalInstance:
|
||||||
|
CipherSpec=NULL
|
||||||
|
GroupName=alpha
|
||||||
|
GroupCipherSpec=ANY_TLS
|
||||||
|
GroupRole=Live
|
||||||
|
GroupLocalAddress=(4445)
|
||||||
|
NativeHAInstance:
|
||||||
|
Name=test-config-instance0
|
||||||
|
ReplicationAddress=test-config-instance0(9145)
|
||||||
|
NativeHAInstance:
|
||||||
|
Name=test-config-instance1
|
||||||
|
ReplicationAddress=test-config-instance1(9145)
|
||||||
|
NativeHAInstance:
|
||||||
|
Name=test-config-instance2
|
||||||
|
ReplicationAddress=test-config-instance2(9145)
|
||||||
|
NativeHARecoveryGroup:
|
||||||
|
GroupName=beta
|
||||||
|
Enabled=Yes
|
||||||
|
ReplicationAddress=beta-address(4445)
|
||||||
16
internal/ha/test_fixtures/envcfg/group-recovery-minimal.ini
Normal file
16
internal/ha/test_fixtures/envcfg/group-recovery-minimal.ini
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
NativeHALocalInstance:
|
||||||
|
GroupName=beta
|
||||||
|
GroupRole=Recovery
|
||||||
|
NativeHAInstance:
|
||||||
|
Name=test-config-instance0
|
||||||
|
ReplicationAddress=test-config-instance0(9145)
|
||||||
|
NativeHAInstance:
|
||||||
|
Name=test-config-instance1
|
||||||
|
ReplicationAddress=test-config-instance1(9145)
|
||||||
|
NativeHAInstance:
|
||||||
|
Name=test-config-instance2
|
||||||
|
ReplicationAddress=test-config-instance2(9145)
|
||||||
|
NativeHARecoveryGroup:
|
||||||
|
GroupName=alpha
|
||||||
|
Enabled=Yes
|
||||||
|
ReplicationAddress=alpha-address(4445)
|
||||||
10
internal/ha/test_fixtures/envcfg/minimal-config.ini
Normal file
10
internal/ha/test_fixtures/envcfg/minimal-config.ini
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
NativeHALocalInstance:
|
||||||
|
NativeHAInstance:
|
||||||
|
Name=test-config-instance0
|
||||||
|
ReplicationAddress=test-config-instance0(9145)
|
||||||
|
NativeHAInstance:
|
||||||
|
Name=test-config-instance1
|
||||||
|
ReplicationAddress=test-config-instance1(9145)
|
||||||
|
NativeHAInstance:
|
||||||
|
Name=test-config-instance2
|
||||||
|
ReplicationAddress=test-config-instance2(9145)
|
||||||
11
internal/ha/test_fixtures/envcfg/tls-full.ini
Normal file
11
internal/ha/test_fixtures/envcfg/tls-full.ini
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
NativeHALocalInstance:
|
||||||
|
CipherSpec=some-cipher
|
||||||
|
NativeHAInstance:
|
||||||
|
Name=test-config-instance0
|
||||||
|
ReplicationAddress=test-config-instance0(9145)
|
||||||
|
NativeHAInstance:
|
||||||
|
Name=test-config-instance1
|
||||||
|
ReplicationAddress=test-config-instance1(9145)
|
||||||
|
NativeHAInstance:
|
||||||
|
Name=test-config-instance2
|
||||||
|
ReplicationAddress=test-config-instance2(9145)
|
||||||
3
internal/ha/test_fixtures/instance/fips.ini
Normal file
3
internal/ha/test_fixtures/instance/fips.ini
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
NativeHALocalInstance:
|
||||||
|
Name=test-config
|
||||||
|
SSLFipsRequired=Yes
|
||||||
3
internal/ha/test_fixtures/instance/no-fips.ini
Normal file
3
internal/ha/test_fixtures/instance/no-fips.ini
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
NativeHALocalInstance:
|
||||||
|
Name=test-config
|
||||||
|
SSLFipsRequired=No
|
||||||
3
internal/ha/test_fixtures/keystore/group-only.ini
Normal file
3
internal/ha/test_fixtures/keystore/group-only.ini
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
NativeHALocalInstance:
|
||||||
|
GroupCertificateLabel=recoveryTLS
|
||||||
|
KeyRepository=/run/runmqserver/ha/tls/key
|
||||||
4
internal/ha/test_fixtures/keystore/ha-group.ini
Normal file
4
internal/ha/test_fixtures/keystore/ha-group.ini
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
NativeHALocalInstance:
|
||||||
|
CertificateLabel=baseTLS
|
||||||
|
GroupCertificateLabel=recoveryTLS
|
||||||
|
KeyRepository=/run/runmqserver/ha/tls/key
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user