From e77ac9617dd36e98d330be22aa9197cbaa14fb8c Mon Sep 17 00:00:00 2001 From: Stephen Marshall Date: Wed, 25 Nov 2020 20:00:11 +0000 Subject: [PATCH 1/8] Add new native HA feature --- Dockerfile-server | 1 + cmd/chkmqhealthy/main.go | 2 +- cmd/runmqserver/main.go | 9 ++++++ cmd/runmqserver/qmgr.go | 8 +++++ ha/native-ha.ini.tpl | 18 ++++++++++++ internal/ha/ha.go | 63 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 ha/native-ha.ini.tpl create mode 100644 internal/ha/ha.go diff --git a/Dockerfile-server b/Dockerfile-server index 531dacb..b9bcbb1 100644 --- a/Dockerfile-server +++ b/Dockerfile-server @@ -93,6 +93,7 @@ RUN mkdir -p /run/runmqserver \ COPY --from=builder $GO_WORKDIR/runmqserver /usr/local/bin/ COPY --from=builder $GO_WORKDIR/chkmq* /usr/local/bin/ COPY NOTICES.txt /opt/mqm/licenses/notices-container.txt +COPY ha/native-ha.ini.tpl /etc/mqm/native-ha.ini.tpl # Copy web XML files COPY web /etc/mqm/web COPY etc/mqm/*.tpl /etc/mqm/ diff --git a/cmd/chkmqhealthy/main.go b/cmd/chkmqhealthy/main.go index 7132bda..607234f 100644 --- a/cmd/chkmqhealthy/main.go +++ b/cmd/chkmqhealthy/main.go @@ -41,7 +41,7 @@ func queueManagerHealthy() (bool, error) { fmt.Println(err) return false, err } - if !strings.Contains(string(out), "(RUNNING)") && !strings.Contains(string(out), "(RUNNING AS STANDBY)") && !strings.Contains(string(out), "(STARTING)") { + if !strings.Contains(string(out), "(RUNNING)") && !strings.Contains(string(out), "(RUNNING AS STANDBY)") && !strings.Contains(string(out), "(STARTING)") && !strings.Contains(string(out), "(REPLICA)") { return false, nil } return true, nil diff --git a/cmd/runmqserver/main.go b/cmd/runmqserver/main.go index 13ea28c..c979550 100644 --- a/cmd/runmqserver/main.go +++ b/cmd/runmqserver/main.go @@ -24,6 +24,7 @@ import ( "os" "sync" + "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/tls" @@ -161,6 +162,14 @@ func doMain() error { return err } + if os.Getenv("MQ_NATIVE_HA") == "true" { + err = ha.ConfigureNativeHA(log) + if err != nil { + logTermination(err) + return err + } + } + newQM, err := createQueueManager(name, *devFlag) if err != nil { logTermination(err) diff --git a/cmd/runmqserver/qmgr.go b/cmd/runmqserver/qmgr.go index b87d40c..1311a5b 100644 --- a/cmd/runmqserver/qmgr.go +++ b/cmd/runmqserver/qmgr.go @@ -113,9 +113,13 @@ func startQueueManager(name string) error { 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 %v starting queue manager: %v", rc, string(out)) return err @@ -220,6 +224,10 @@ func getCreateQueueManagerArgs(mounts map[string]string, name string, devMode bo //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) } diff --git a/ha/native-ha.ini.tpl b/ha/native-ha.ini.tpl new file mode 100644 index 0000000..b6be471 --- /dev/null +++ b/ha/native-ha.ini.tpl @@ -0,0 +1,18 @@ +NativeHALocalInstance: + Name={{ .Name }} + {{ if .CertificateLabel }} + CertificateLabel={{ .CertificateLabel }} + KeyRepository={{ .KeyRepository }} + {{ if .CipherSpec }} + CipherSpec={{ .CipherSpec }} + {{- end }} + {{- end }} +NativeHAInstance: + Name={{ .NativeHAInstance0_Name }} + ReplicationAddress={{ .NativeHAInstance0_ReplicationAddress }} +NativeHAInstance: + Name={{ .NativeHAInstance1_Name }} + ReplicationAddress={{ .NativeHAInstance1_ReplicationAddress }} +NativeHAInstance: + Name={{ .NativeHAInstance2_Name }} + ReplicationAddress={{ .NativeHAInstance2_ReplicationAddress }} diff --git a/internal/ha/ha.go b/internal/ha/ha.go new file mode 100644 index 0000000..41e13fe --- /dev/null +++ b/internal/ha/ha.go @@ -0,0 +1,63 @@ +/* +© Copyright IBM Corporation 2020 + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package ha contains code for high availability +package ha + +import ( + "os" + + "github.com/ibm-messaging/mq-container/internal/mqtemplate" + "github.com/ibm-messaging/mq-container/pkg/logger" +) + +// ConfigureNativeHA configures native high availability +func ConfigureNativeHA(log *logger.Logger) error { + + file := "/etc/mqm/native-ha.ini" + templateFile := file + ".tpl" + + templateMap := map[string]string{} + templateMap["Name"] = os.Getenv("HOSTNAME") + templateMap["NativeHAInstance0_Name"] = os.Getenv("MQ_NATIVE_HA_INSTANCE_0_NAME") + templateMap["NativeHAInstance1_Name"] = os.Getenv("MQ_NATIVE_HA_INSTANCE_1_NAME") + templateMap["NativeHAInstance2_Name"] = os.Getenv("MQ_NATIVE_HA_INSTANCE_2_NAME") + templateMap["NativeHAInstance0_ReplicationAddress"] = os.Getenv("MQ_NATIVE_HA_INSTANCE_0_REPLICATION_ADDRESS") + templateMap["NativeHAInstance1_ReplicationAddress"] = os.Getenv("MQ_NATIVE_HA_INSTANCE_1_REPLICATION_ADDRESS") + templateMap["NativeHAInstance2_ReplicationAddress"] = os.Getenv("MQ_NATIVE_HA_INSTANCE_2_REPLICATION_ADDRESS") + + if os.Getenv("MQ_NATIVE_HA_TLS") == "true" { + templateMap["CertificateLabel"] = os.Getenv("MQ_NATIVE_HA_TLS_CERTLABEL") + + keyRepository, ok := os.LookupEnv("MQ_NATIVE_HA_KEY_REPOSITORY") + if !ok { + keyRepository = "/run/runmqserver/tls/key" + } + templateMap["KeyRepository"] = keyRepository + + cipherSpec, ok := os.LookupEnv("MQ_NATIVE_HA_CIPHERSPEC") + if ok { + templateMap["CipherSpec"] = cipherSpec + } + } + + err := mqtemplate.ProcessTemplateFile(templateFile, file, templateMap, log) + if err != nil { + return err + } + + return nil +} From adf15b7bd3558206ffcc5b1c04a7dc582e17a395 Mon Sep 17 00:00:00 2001 From: Stephen Marshall Date: Wed, 2 Dec 2020 17:56:42 +0000 Subject: [PATCH 2/8] Update to MQ 9.2.2.0 --- .travis.yml | 14 +++++++------- Dockerfile-server | 2 +- config.env | 2 +- docs/building.md | 4 ++-- docs/security.md | 4 ++-- docs/testing.md | 4 ++-- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2952fc7..8a6ebd3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -44,7 +44,7 @@ jobs: name: "Basic AMD64 build" os: linux env: - - MQ_ARCHIVE_REPOSITORY_DEV=$MQ_921_ARCHIVE_REPOSITORY_DEV_AMD64 + - MQ_ARCHIVE_REPOSITORY_DEV=$MQ_922_ARCHIVE_REPOSITORY_DEV_AMD64 script: bash -e travis-build-scripts/run.sh # CD Build @@ -60,8 +60,8 @@ jobs: os: linux env: - BUILD_ALL=true - - MQ_ARCHIVE_REPOSITORY=$MQ_921_ARCHIVE_REPOSITORY_AMD64 - - MQ_ARCHIVE_REPOSITORY_DEV=$MQ_921_ARCHIVE_REPOSITORY_DEV_AMD64 + - MQ_ARCHIVE_REPOSITORY=$MQ_922_ARCHIVE_REPOSITORY_AMD64 + - MQ_ARCHIVE_REPOSITORY_DEV=$MQ_922_ARCHIVE_REPOSITORY_DEV_AMD64 script: bash -e travis-build-scripts/run.sh # - if: branch = private-master OR tag =~ ^release-candidate* # name: "Multi-Arch PPC64LE build" @@ -69,8 +69,8 @@ jobs: # env: # - BUILD_ALL=true # - TEST_OPTS_DOCKER="-run TestGoldenPathWithMetrics" - # # - MQ_ARCHIVE_REPOSITORY=$MQ_921_ARCHIVE_REPOSITORY_PPC64LE - # - MQ_ARCHIVE_REPOSITORY_DEV=$MQ_921_ARCHIVE_REPOSITORY_DEV_PPC64LE + # # - MQ_ARCHIVE_REPOSITORY=$MQ_922_ARCHIVE_REPOSITORY_PPC64LE + # - MQ_ARCHIVE_REPOSITORY_DEV=$MQ_922_ARCHIVE_REPOSITORY_DEV_PPC64LE # script: bash -e travis-build-scripts/run.sh - stage: build if: branch = private-master OR tag =~ ^release-candidate* @@ -79,8 +79,8 @@ jobs: env: - BUILD_ALL=true - TEST_OPTS_DOCKER="-run TestGoldenPathWithMetrics" - - MQ_ARCHIVE_REPOSITORY=$MQ_921_ARCHIVE_REPOSITORY_S390X - - MQ_ARCHIVE_REPOSITORY_DEV=$MQ_921_ARCHIVE_REPOSITORY_DEV_S390X + - MQ_ARCHIVE_REPOSITORY=$MQ_922_ARCHIVE_REPOSITORY_S390X + - MQ_ARCHIVE_REPOSITORY_DEV=$MQ_922_ARCHIVE_REPOSITORY_DEV_S390X script: bash -e travis-build-scripts/run.sh - stage: push-manifest if: branch = private-master AND type != pull_request OR tag =~ ^release-candidate* diff --git a/Dockerfile-server b/Dockerfile-server index b9bcbb1..4225dd1 100644 --- a/Dockerfile-server +++ b/Dockerfile-server @@ -15,7 +15,7 @@ ARG BASE_IMAGE=registry.redhat.io/ubi8/ubi-minimal ARG BASE_TAG=8.3-230 ARG GO_WORKDIR=/go/src/github.com/ibm-messaging/mq-container -ARG MQ_URL="https://public.dhe.ibm.com/ibmdl/export/pub/software/websphere/messaging/mqadv/9.2.1.0-IBM-MQ-Advanced-for-Developers-Non-Install-LinuxX64.tar.gz" +ARG MQ_URL="https://public.dhe.ibm.com/ibmdl/export/pub/software/websphere/messaging/mqadv/9.2.2.0-IBM-MQ-Advanced-for-Developers-Non-Install-LinuxX64.tar.gz" ############################################################################### # Build stage to build Go code ############################################################################### diff --git a/config.env b/config.env index 40925a4..0c336b7 100644 --- a/config.env +++ b/config.env @@ -1,6 +1,6 @@ ########################################################################################################################################################### # MQ_VERSION is the fully qualified MQ version number to build -MQ_VERSION ?= 9.2.1.0 +MQ_VERSION ?= 9.2.2.0 ########################################################################################################################################################### diff --git a/docs/building.md b/docs/building.md index 2585bf1..cc1da6d 100644 --- a/docs/building.md +++ b/docs/building.md @@ -9,7 +9,7 @@ You need to have the following tools installed: If you are working in the Windows Subsystem for Linux, follow [this guide by Microsoft to set up Docker](https://blogs.msdn.microsoft.com/commandline/2017/12/08/cross-post-wsl-interoperability-with-docker/) first. -You will also need a [Red Hat Account](https://access.redhat.com) to be able to access the Red Hat Registry. +You will also need a [Red Hat Account](https://access.redhat.com) to be able to access the Red Hat Registry. ## Building a production image @@ -20,7 +20,7 @@ From MQ 9.2.X, the MQ container adds support for MQ Long Term Support (LTS) **pr This procedure works for building the MQ Continuous Delivery release, on `amd64`, `ppc64le` and `s390x` architectures. 1. Create a `downloads` directory in the root of this repository -2. Download MQ from [IBM Passport Advantage](https://www.ibm.com/software/passportadvantage/) or [IBM Fix Central](https://www.ibm.com/support/fixcentral), and place the downloaded file (for example, `IBM_MQ_9.2.1_LINUX_X86-64_NOINST.tar.gz`) in the `downloads` directory +2. Download MQ from [IBM Passport Advantage](https://www.ibm.com/software/passportadvantage/) or [IBM Fix Central](https://www.ibm.com/support/fixcentral), and place the downloaded file (for example, `IBM_MQ_9.2.2_LINUX_X86-64_NOINST.tar.gz`) in the `downloads` directory 3. Login to the Red Hat Registry: `docker login registry.redhat.io` using your Customer Portal credentials. 4. Run `make build-advancedserver` diff --git a/docs/security.md b/docs/security.md index fa63852..e3c961c 100644 --- a/docs/security.md +++ b/docs/security.md @@ -16,7 +16,7 @@ docker run \ --env LICENSE=accept \ --env MQ_QMGR_NAME=QM1 \ --detach \ - ibm-mqadvanced-server:9.2.1.0-amd64 + ibm-mqadvanced-server:9.2.2.0-amd64 ``` The MQ Advanced for Developers image does require the "chown", "setuid", "setgid" and "audit_write" capabilities (plus "dac_override" if you're using an image based on Red Hat Enterprise Linux). This is because it uses the "sudo" command to change passwords inside the container. For example, in Docker, you could do the following: @@ -31,5 +31,5 @@ docker run \ --env LICENSE=accept \ --env MQ_QMGR_NAME=QM1 \ --detach \ - ibm-mqadvanced-server-dev:9.2.1.0-amd64 + ibm-mqadvanced-server-dev:9.2.2.0-amd64 ``` diff --git a/docs/testing.md b/docs/testing.md index 05d2a0b..7ca41c4 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -14,7 +14,7 @@ There are two main sets of tests: 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: ``` @@ -25,7 +25,7 @@ make 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.2.1.0-amd64 make test-advancedserver +MQ_IMAGE_ADVANCEDSERVER=ibm-mqadvanced-server:9.2.2.0-amd64 make test-advancedserver ``` You can pass parameters to `go test` with an environment variable. For example, to run the "TestGoldenPath" test, run the following command: From 68fe4a1dc1f0ef029fe61de07be18b2f664d37df Mon Sep 17 00:00:00 2001 From: Stephen Marshall Date: Mon, 7 Dec 2020 20:04:59 +0000 Subject: [PATCH 3/8] Update ready check for native-HA --- cmd/chkmqready/main.go | 9 +++++++-- internal/ready/ready.go | 5 +++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/cmd/chkmqready/main.go b/cmd/chkmqready/main.go index 9bc5487..8f102df 100644 --- a/cmd/chkmqready/main.go +++ b/cmd/chkmqready/main.go @@ -39,7 +39,7 @@ func main() { } // Check if the queue manager has a running listener - if standby, _ := ready.IsRunningAsStandbyQM(name); !standby { + if active, _ := ready.IsRunningAsActiveQM(name); active { conn, err := net.Dial("tcp", "127.0.0.1:1414") if err != nil { fmt.Println(err) @@ -49,8 +49,13 @@ func main() { if err != nil { fmt.Println(err) } - } else { + } else if standby, _ := ready.IsRunningAsStandbyQM(name); standby { fmt.Printf("Detected queue manager running in standby mode") os.Exit(10) + } else if replica, _ := ready.IsRunningAsReplicaQM(name); replica { + fmt.Printf("Detected queue manager running in replica mode") + os.Exit(20) + } else { + os.Exit(1) } } diff --git a/internal/ready/ready.go b/internal/ready/ready.go index d866878..22df1a2 100644 --- a/internal/ready/ready.go +++ b/internal/ready/ready.go @@ -76,6 +76,11 @@ func IsRunningAsStandbyQM(name string) (bool, error) { return isRunningQM(name, "(RUNNING AS STANDBY)") } +// IsRunningAsReplicaQM returns true if the queue manager is running in replica mode +func IsRunningAsReplicaQM(name string) (bool, error) { + return isRunningQM(name, "(REPLICA)") +} + func isRunningQM(name string, status string) (bool, error) { out, _, err := command.Run("dspmq", "-n", "-m", name) if err != nil { From e1c96655b193ddc8dc43f4188b7f46b67d76a33c Mon Sep 17 00:00:00 2001 From: Luke Powlett Date: Wed, 16 Dec 2020 17:03:13 +0000 Subject: [PATCH 4/8] Setup native-ha branch as main branch --- .travis.yml | 22 +++++++++++----------- Makefile | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8a6ebd3..6da8df6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,7 +25,7 @@ services: env: global: - - MAIN_BRANCH=private-master + - MAIN_BRANCH=native-ha - MQ_LTS_VERSION=9.2.0.1 - TAGCACHE_FILE=tagcache - RELEASE=r1 @@ -40,7 +40,7 @@ go_import_path: "github.com/ibm-messaging/mq-container" jobs: include: - stage: basic-build - if: branch != private-master AND tag IS blank + if: branch != native-ha AND tag IS blank name: "Basic AMD64 build" os: linux env: @@ -50,12 +50,12 @@ jobs: # CD Build - stage: global-tag - if: branch = private-master AND type != pull_request OR tag =~ ^release-candidate* + if: branch = native-ha 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 = private-master OR tag =~ ^release-candidate* + if: branch = native-ha OR tag =~ ^release-candidate* name: "Multi-Arch AMD64 build" os: linux env: @@ -63,7 +63,7 @@ jobs: - MQ_ARCHIVE_REPOSITORY=$MQ_922_ARCHIVE_REPOSITORY_AMD64 - MQ_ARCHIVE_REPOSITORY_DEV=$MQ_922_ARCHIVE_REPOSITORY_DEV_AMD64 script: bash -e travis-build-scripts/run.sh - # - if: branch = private-master OR tag =~ ^release-candidate* + # - if: branch = native-ha OR tag =~ ^release-candidate* # name: "Multi-Arch PPC64LE build" # os: linux-ppc64le # env: @@ -73,7 +73,7 @@ jobs: # - MQ_ARCHIVE_REPOSITORY_DEV=$MQ_922_ARCHIVE_REPOSITORY_DEV_PPC64LE # script: bash -e travis-build-scripts/run.sh - stage: build - if: branch = private-master OR tag =~ ^release-candidate* + if: branch = native-ha OR tag =~ ^release-candidate* name: "Multi-Arch S390X build" os: linux-s390 env: @@ -83,7 +83,7 @@ jobs: - MQ_ARCHIVE_REPOSITORY_DEV=$MQ_922_ARCHIVE_REPOSITORY_DEV_S390X script: bash -e travis-build-scripts/run.sh - stage: push-manifest - if: branch = private-master AND type != pull_request OR tag =~ ^release-candidate* + if: branch = native-ha AND type != pull_request OR tag =~ ^release-candidate* name: "Push Manifest-list to registry" env: - PUSH_MANIFEST_ONLY=true @@ -92,7 +92,7 @@ jobs: # LTS Build - stage: global-tag - if: branch = private-master AND type != pull_request OR tag =~ ^release-candidate* + if: branch = native-ha AND type != pull_request OR tag =~ ^release-candidate* name: "Generate Global Tag" os: linux env: @@ -102,7 +102,7 @@ jobs: - RELEASE=$RELEASE_LTS script: bash -e travis-build-scripts/global-tag.sh - stage: build - if: branch = private-master OR tag =~ ^release-candidate* + if: branch = native-ha OR tag =~ ^release-candidate* name: "Multi-Arch AMD64 build" os: linux env: @@ -113,7 +113,7 @@ jobs: - RELEASE=$RELEASE_LTS script: bash -e travis-build-scripts/run.sh - stage: build - if: branch = private-master OR tag =~ ^release-candidate* + if: branch = native-ha OR tag =~ ^release-candidate* name: "Multi-Arch S390X build" os: linux-s390 env: @@ -125,7 +125,7 @@ jobs: - RELEASE=$RELEASE_LTS script: bash -e travis-build-scripts/run.sh - stage: push-manifest - if: branch = private-master AND type != pull_request OR tag =~ ^release-candidate* + if: branch = native-ha AND type != pull_request OR tag =~ ^release-candidate* name: "Push Manifest-list to registry" env: - LTS=true diff --git a/Makefile b/Makefile index f9d230f..ca0b86e 100644 --- a/Makefile +++ b/Makefile @@ -264,7 +264,7 @@ build-devjmstest: test-devserver: test/docker/vendor $(info $(SPACER)$(shell printf $(TITLE)"Test $(MQ_IMAGE_DEVSERVER):$(MQ_TAG) on $(shell docker --version)"$(END))) docker inspect $(MQ_IMAGE_DEVSERVER):$(MQ_TAG) - cd test/docker && TEST_IMAGE=$(MQ_IMAGE_DEVSERVER):$(MQ_TAG) EXPECTED_LICENSE=Developer DEV_JMS_IMAGE=$(DEV_JMS_IMAGE) IBMJRE=true go test -parallel $(NUM_CPU) -timeout $(TEST_TIMEOUT_DOCKER) -tags mqdev $(TEST_OPTS_DOCKER) + cd test/docker && TEST_IMAGE=$(MQ_IMAGE_DEVSERVER):$(MQ_TAG) EXPECTED_LICENSE=Production DEV_JMS_IMAGE=$(DEV_JMS_IMAGE) IBMJRE=true go test -parallel $(NUM_CPU) -timeout $(TEST_TIMEOUT_DOCKER) -tags mqdev $(TEST_OPTS_DOCKER) .PHONY: coverage coverage: From 1d41f4b13829a613fc64e7aa79adcfca97df384f Mon Sep 17 00:00:00 2001 From: Luke Powlett Date: Wed, 6 Jan 2021 16:02:00 +0000 Subject: [PATCH 5/8] Added NativeHA TLS keystore --- cmd/runmqserver/main.go | 6 +- internal/ha/ha.go | 14 +++- internal/tls/tls.go | 138 ++++++++++++++++++++++++++++------------ internal/tls/tls_web.go | 2 +- 4 files changed, 113 insertions(+), 47 deletions(-) diff --git a/cmd/runmqserver/main.go b/cmd/runmqserver/main.go index c979550..99108c8 100644 --- a/cmd/runmqserver/main.go +++ b/cmd/runmqserver/main.go @@ -144,19 +144,19 @@ func doMain() error { // Print out versioning information logVersionInfo() - keyLabel, cmsKeystore, p12Truststore, err := tls.ConfigureTLSKeystores() + keyLabel, defaultCmsKeystore, defaultP12Truststore, err := tls.ConfigureDefaultTLSKeystores() if err != nil { logTermination(err) return err } - err = tls.ConfigureTLS(keyLabel, cmsKeystore, *devFlag, log) + err = tls.ConfigureTLS(keyLabel, defaultCmsKeystore, *devFlag, log) if err != nil { logTermination(err) return err } - err = postInit(name, keyLabel, p12Truststore) + err = postInit(name, keyLabel, defaultP12Truststore) if err != nil { logTermination(err) return err diff --git a/internal/ha/ha.go b/internal/ha/ha.go index 41e13fe..5d98ac7 100644 --- a/internal/ha/ha.go +++ b/internal/ha/ha.go @@ -21,6 +21,7 @@ import ( "os" "github.com/ibm-messaging/mq-container/internal/mqtemplate" + "github.com/ibm-messaging/mq-container/internal/tls" "github.com/ibm-messaging/mq-container/pkg/logger" ) @@ -40,11 +41,20 @@ func ConfigureNativeHA(log *logger.Logger) error { templateMap["NativeHAInstance2_ReplicationAddress"] = os.Getenv("MQ_NATIVE_HA_INSTANCE_2_REPLICATION_ADDRESS") if os.Getenv("MQ_NATIVE_HA_TLS") == "true" { - templateMap["CertificateLabel"] = os.Getenv("MQ_NATIVE_HA_TLS_CERTLABEL") + keyLabel, _, err := tls.ConfigureHATLSKeystore() + if err != nil { + return err + } + + certLabel, ok := os.LookupEnv("MQ_NATIVE_HA_TLS_CERTLABEL") + if !ok { + certLabel = keyLabel + } + templateMap["CertificateLabel"] = certLabel keyRepository, ok := os.LookupEnv("MQ_NATIVE_HA_KEY_REPOSITORY") if !ok { - keyRepository = "/run/runmqserver/tls/key" + keyRepository = "/run/runmqserver/ha/tls/key" } templateMap["KeyRepository"] = keyRepository diff --git a/internal/tls/tls.go b/internal/tls/tls.go index 614e696..08fd287 100644 --- a/internal/tls/tls.go +++ b/internal/tls/tls.go @@ -43,14 +43,20 @@ const cmsKeystoreName = "key.kdb" // p12TruststoreName is the name of the PKCS#12 Truststore const p12TruststoreName = "trust.p12" -// keystoreDir is the location for the CMS Keystore & PKCS#12 Truststore -const keystoreDir = "/run/runmqserver/tls/" +// keystoreDir is the location for the default CMS Keystore & PKCS#12 Truststore +const keystoreDirDefault = "/run/runmqserver/tls/" + +// keystoreDirHA is the location for the HA CMS Keystore +const keystoreDirHA = "/run/runmqserver/ha/tls/" // keyDir is the location of the keys to import -const keyDir = "/etc/mqm/pki/keys" +const keyDirDefault = "/etc/mqm/pki/keys" + +// keyDir is the location of the HA keys to import +const keyDirHA = "/etc/mqm/ha/pki/keys" // trustDir is the location of the trust certificates to import -const trustDir = "/etc/mqm/pki/trust" +const trustDirDefault = "/etc/mqm/pki/trust" type KeyStoreData struct { Keystore *keystore.KeyStore @@ -65,28 +71,51 @@ type P12KeyFiles struct { Password string } -// ConfigureTLSKeystores configures the CMS Keystore & PKCS#12 Truststore -func ConfigureTLSKeystores() (string, KeyStoreData, KeyStoreData, error) { +type TLSStore struct { + Keystore KeyStoreData + Truststore KeyStoreData +} + +// ConfigureDefaultTLSKeystores configures the CMS Keystore & PKCS#12 Truststore +func ConfigureDefaultTLSKeystores() (string, KeyStoreData, KeyStoreData, error) { // Create the CMS Keystore & PKCS#12 Truststore - cmsKeystore, p12Truststore, err := generateAllKeystores() + tlsStore, err := generateAllDefaultKeystores() if err != nil { - return "", cmsKeystore, p12Truststore, err + return "", tlsStore.Keystore, tlsStore.Truststore, err } // Process all keys - add them to the CMS KeyStore - keyLabel, err := processKeys(&cmsKeystore, &p12Truststore) + keyLabel, err := processKeys(&tlsStore, keystoreDirDefault, keyDirDefault) if err != nil { - return "", cmsKeystore, p12Truststore, err + return "", tlsStore.Keystore, tlsStore.Truststore, err } // Process all trust certificates - add them to the CMS KeyStore & PKCS#12 Truststore - err = processTrustCertificates(&cmsKeystore, &p12Truststore) + err = processTrustCertificates(&tlsStore, trustDirDefault) if err != nil { - return "", cmsKeystore, p12Truststore, err + return "", tlsStore.Keystore, tlsStore.Truststore, err } - return keyLabel, cmsKeystore, p12Truststore, err + return keyLabel, tlsStore.Keystore, tlsStore.Truststore, err +} + +// ConfigureHATLSKeystore configures the CMS Keystore & PKCS#12 Truststore +func ConfigureHATLSKeystore() (string, KeyStoreData, error) { + + // Create the CMS Keystore & PKCS#12 Truststore + tlsStore, err := generateHAKeystore() + if err != nil { + return "", tlsStore.Keystore, err + } + + // Process all keys - add them to the CMS KeyStore + keyLabel, err := processKeys(&tlsStore, keystoreDirHA, keyDirHA) + if err != nil { + return "", tlsStore.Keystore, err + } + + return keyLabel, tlsStore.Keystore, err } // ConfigureTLS configures TLS for the queue manager @@ -137,8 +166,9 @@ func configureTLSDev(log *logger.Logger) error { return nil } -// generateAllKeystores creates the CMS Keystore & PKCS#12 Truststore -func generateAllKeystores() (KeyStoreData, KeyStoreData, error) { +// generateAllDefaultKeystores creates the CMS Keystore & PKCS#12 Truststore +func generateAllDefaultKeystores() (TLSStore, error) { + var cmsKeystore, p12Truststore KeyStoreData // Generate a pasword for use with both the CMS Keystore & PKCS#12 Truststore @@ -148,30 +178,54 @@ func generateAllKeystores() (KeyStoreData, KeyStoreData, error) { // Create the Keystore directory - if it does not already exist // #nosec G301 - write group permissions are required - err := os.MkdirAll(keystoreDir, 0770) + err := os.MkdirAll(keystoreDirDefault, 0770) if err != nil { - return cmsKeystore, p12Truststore, fmt.Errorf("Failed to create Keystore directory: %v", err) + return TLSStore{cmsKeystore, p12Truststore}, fmt.Errorf("Failed to create Keystore directory: %v", err) } // Create the CMS Keystore - cmsKeystore.Keystore = keystore.NewCMSKeyStore(filepath.Join(keystoreDir, cmsKeystoreName), cmsKeystore.Password) + cmsKeystore.Keystore = keystore.NewCMSKeyStore(filepath.Join(keystoreDirDefault, cmsKeystoreName), cmsKeystore.Password) err = cmsKeystore.Keystore.Create() if err != nil { - return cmsKeystore, p12Truststore, fmt.Errorf("Failed to create CMS Keystore: %v", err) + return TLSStore{cmsKeystore, p12Truststore}, fmt.Errorf("Failed to create CMS Keystore: %v", err) } // Create the PKCS#12 Truststore - p12Truststore.Keystore = keystore.NewPKCS12KeyStore(filepath.Join(keystoreDir, p12TruststoreName), p12Truststore.Password) + p12Truststore.Keystore = keystore.NewPKCS12KeyStore(filepath.Join(keystoreDirDefault, p12TruststoreName), p12Truststore.Password) err = p12Truststore.Keystore.Create() if err != nil { - return cmsKeystore, p12Truststore, fmt.Errorf("Failed to create PKCS#12 Truststore: %v", err) + return TLSStore{cmsKeystore, p12Truststore}, fmt.Errorf("Failed to create PKCS#12 Truststore: %v", err) + } + return TLSStore{cmsKeystore, p12Truststore}, nil +} + +// generateHAKeystore creates the CMS Keystore for Native HA replication +func generateHAKeystore() (TLSStore, error) { + var cmsKeystore KeyStoreData + + // Generate a pasword for use with the CMS Keystore + pw := generateRandomPassword() + cmsKeystore.Password = pw + + // Create the Keystore directory - if it does not already exist + // #nosec G301 - write group permissions are required + err := os.MkdirAll(keystoreDirHA, 0770) + if err != nil { + return TLSStore{Keystore: cmsKeystore}, fmt.Errorf("Failed to create HA Keystore directory: %v", err) } - return cmsKeystore, p12Truststore, nil + // Create the CMS Keystore + cmsKeystore.Keystore = keystore.NewCMSKeyStore(filepath.Join(keystoreDirHA, cmsKeystoreName), cmsKeystore.Password) + err = cmsKeystore.Keystore.Create() + if err != nil { + return TLSStore{Keystore: cmsKeystore}, fmt.Errorf("Failed to create CMS Keystore: %v", err) + } + + return TLSStore{Keystore: cmsKeystore}, nil } // processKeys processes all keys - adding them to the CMS KeyStore -func processKeys(cmsKeystore, p12Truststore *KeyStoreData) (string, error) { +func processKeys(tlsStore *TLSStore, keystoreDir string, keyDir string) (string, error) { // Key label - will be set to the label of the first set of keys keyLabel := "" @@ -190,7 +244,7 @@ func processKeys(cmsKeystore, p12Truststore *KeyStoreData) (string, error) { } // Process private key (*.key) - privateKey, keyPrefix, err := processPrivateKey(keySet.Name(), keys) + privateKey, keyPrefix, err := processPrivateKey(keyDir, keySet.Name(), keys) if err != nil { return "", err } @@ -201,13 +255,13 @@ func processKeys(cmsKeystore, p12Truststore *KeyStoreData) (string, error) { } // Process certificates (*.crt) - public certificate & optional CA certificate - publicCertificate, caCertificate, err := processCertificates(keySet.Name(), keyPrefix, keys, cmsKeystore, p12Truststore) + publicCertificate, caCertificate, err := processCertificates(keyDir, keySet.Name(), keyPrefix, keys, &tlsStore.Keystore, &tlsStore.Truststore) if err != nil { return "", err } // Create a new PKCS#12 Keystore - containing private key, public certificate & optional CA certificate - file, err := pkcs.Encode(rand.Reader, privateKey, publicCertificate, caCertificate, cmsKeystore.Password) + file, err := pkcs.Encode(rand.Reader, privateKey, publicCertificate, caCertificate, tlsStore.Keystore.Password) if err != nil { return "", fmt.Errorf("Failed to encode PKCS#12 Keystore %s: %v", keySet.Name()+".p12", err) } @@ -217,13 +271,13 @@ func processKeys(cmsKeystore, p12Truststore *KeyStoreData) (string, error) { } // Import the new PKCS#12 Keystore into the CMS Keystore - err = cmsKeystore.Keystore.Import(filepath.Join(keystoreDir, keySet.Name()+".p12"), cmsKeystore.Password) + err = tlsStore.Keystore.Keystore.Import(filepath.Join(keystoreDir, keySet.Name()+".p12"), tlsStore.Keystore.Password) if err != nil { return "", fmt.Errorf("Failed tp import keys from %s into CMS Keystore: %v", filepath.Join(keystoreDir, keySet.Name()+".p12"), err) } // Relabel the certificate in the CMS Keystore - err = relabelCertificate(keySet.Name(), cmsKeystore) + err = relabelCertificate(keySet.Name(), &tlsStore.Keystore) if err != nil { return "", err } @@ -239,7 +293,7 @@ func processKeys(cmsKeystore, p12Truststore *KeyStoreData) (string, error) { } // processTrustCertificates processes all trust certificates - adding them to the CMS KeyStore & PKCS#12 Truststore -func processTrustCertificates(cmsKeystore, p12Truststore *KeyStoreData) error { +func processTrustCertificates(tlsStore *TLSStore, trustDir string) error { // Process all trust certiifcates trustList, err := ioutil.ReadDir(trustDir) @@ -265,13 +319,13 @@ func processTrustCertificates(cmsKeystore, p12Truststore *KeyStoreData) error { } // Add to known certificates for the CMS Keystore - err = addToKnownCertificates(block, cmsKeystore, true) + err = addToKnownCertificates(block, &tlsStore.Keystore, true) if err != nil { return fmt.Errorf("Failed to add to know certificates for CMS Keystore") } // Add to known certificates for the PKCS#12 Truststore - err = addToKnownCertificates(block, p12Truststore, true) + err = addToKnownCertificates(block, &tlsStore.Truststore, true) if err != nil { return fmt.Errorf("Failed to add to know certificates for PKCS#12 Truststore") } @@ -282,16 +336,16 @@ func processTrustCertificates(cmsKeystore, p12Truststore *KeyStoreData) error { } // Add all trust certificates to PKCS#12 Truststore - if len(p12Truststore.TrustedCerts) > 0 { - err = addCertificatesToTruststore(p12Truststore) + if len(tlsStore.Truststore.TrustedCerts) > 0 { + err = addCertificatesToTruststore(&tlsStore.Truststore) if err != nil { return err } } // Add all trust certificates to CMS Keystore - if len(cmsKeystore.TrustedCerts) > 0 { - err = addCertificatesToCMSKeystore(cmsKeystore) + if len(tlsStore.Keystore.TrustedCerts) > 0 { + err = addCertificatesToCMSKeystore(&tlsStore.Keystore) if err != nil { return err } @@ -301,7 +355,7 @@ func processTrustCertificates(cmsKeystore, p12Truststore *KeyStoreData) error { } // processPrivateKey processes the private key (*.key) from a set of keys -func processPrivateKey(keySetName string, keys []os.FileInfo) (interface{}, string, error) { +func processPrivateKey(keyDir string, keySetName string, keys []os.FileInfo) (interface{}, string, error) { var privateKey interface{} keyPrefix := "" @@ -336,7 +390,7 @@ func processPrivateKey(keySetName string, keys []os.FileInfo) (interface{}, stri } // processCertificates processes the certificates (*.crt) from a set of keys -func processCertificates(keySetName, keyPrefix string, keys []os.FileInfo, cmsKeystore, p12Truststore *KeyStoreData) (*x509.Certificate, []*x509.Certificate, error) { +func processCertificates(keyDir string, keySetName, keyPrefix string, keys []os.FileInfo, cmsKeystore, p12Truststore *KeyStoreData) (*x509.Certificate, []*x509.Certificate, error) { var publicCertificate *x509.Certificate var caCertificate []*x509.Certificate @@ -384,10 +438,12 @@ func processCertificates(keySetName, keyPrefix string, keys []os.FileInfo, cmsKe return nil, nil, fmt.Errorf("Failed to add to know certificates for CMS Keystore") } - // Add to known certificates for the PKCS#12 Truststore - err = addToKnownCertificates(block, p12Truststore, true) - if err != nil { - return nil, nil, fmt.Errorf("Failed to add to know certificates for PKCS#12 Truststore") + if p12Truststore != nil { + // Add to known certificates for the PKCS#12 Truststore + err = addToKnownCertificates(block, p12Truststore, true) + if err != nil { + return nil, nil, fmt.Errorf("Failed to add to know certificates for PKCS#12 Truststore") + } } certificate, err := x509.ParseCertificate(block.Bytes) diff --git a/internal/tls/tls_web.go b/internal/tls/tls_web.go index d22a93e..374aa64 100644 --- a/internal/tls/tls_web.go +++ b/internal/tls/tls_web.go @@ -60,7 +60,7 @@ func ConfigureWebKeystore(p12Truststore KeyStoreData, webKeystore string) (strin if webKeystore == "" { webKeystore = webKeystoreDefault } - webKeystoreFile := filepath.Join(keystoreDir, webKeystore) + webKeystoreFile := filepath.Join(keystoreDirDefault, webKeystore) // Check if a new self-signed certificate should be generated genHostName := os.Getenv("MQ_GENERATE_CERTIFICATE_HOSTNAME") From 8a2faf295538e8dc17e8c1368ec70a25d0301a97 Mon Sep 17 00:00:00 2001 From: Luke Powlett Date: Thu, 14 Jan 2021 14:00:52 +0000 Subject: [PATCH 6/8] Added initial set of NativeHA docker tests, updated HA TLS env --- cmd/runmqserver/main.go | 2 +- internal/ha/ha.go | 9 +- internal/tls/tls.go | 12 +- internal/tls/tls_web.go | 2 +- test/docker/docker_api_test_util.go | 39 +++- test/docker/go.mod | 1 + test/docker/go.sum | 267 ++++++++++++++++++++++++++ test/docker/mq_native_ha_test.go | 219 +++++++++++++++++++++ test/docker/mq_native_ha_test_util.go | 145 ++++++++++++++ 9 files changed, 680 insertions(+), 16 deletions(-) create mode 100644 test/docker/mq_native_ha_test.go create mode 100644 test/docker/mq_native_ha_test_util.go diff --git a/cmd/runmqserver/main.go b/cmd/runmqserver/main.go index 99108c8..41137f5 100644 --- a/cmd/runmqserver/main.go +++ b/cmd/runmqserver/main.go @@ -1,5 +1,5 @@ /* -© Copyright IBM Corporation 2017, 2020 +© Copyright IBM Corporation 2017, 2021 Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/internal/ha/ha.go b/internal/ha/ha.go index 5d98ac7..db7af85 100644 --- a/internal/ha/ha.go +++ b/internal/ha/ha.go @@ -1,5 +1,5 @@ /* -© Copyright IBM Corporation 2020 +© Copyright IBM Corporation 2020, 2021 Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -45,12 +45,7 @@ func ConfigureNativeHA(log *logger.Logger) error { if err != nil { return err } - - certLabel, ok := os.LookupEnv("MQ_NATIVE_HA_TLS_CERTLABEL") - if !ok { - certLabel = keyLabel - } - templateMap["CertificateLabel"] = certLabel + templateMap["CertificateLabel"] = keyLabel keyRepository, ok := os.LookupEnv("MQ_NATIVE_HA_KEY_REPOSITORY") if !ok { diff --git a/internal/tls/tls.go b/internal/tls/tls.go index 08fd287..355d8e2 100644 --- a/internal/tls/tls.go +++ b/internal/tls/tls.go @@ -1,5 +1,5 @@ /* -© Copyright IBM Corporation 2019 +© Copyright IBM Corporation 2019, 2021 Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -43,19 +43,19 @@ const cmsKeystoreName = "key.kdb" // p12TruststoreName is the name of the PKCS#12 Truststore const p12TruststoreName = "trust.p12" -// keystoreDir is the location for the default CMS Keystore & PKCS#12 Truststore +// keystoreDirDefault is the location for the default CMS Keystore & PKCS#12 Truststore const keystoreDirDefault = "/run/runmqserver/tls/" // keystoreDirHA is the location for the HA CMS Keystore const keystoreDirHA = "/run/runmqserver/ha/tls/" -// keyDir is the location of the keys to import +// keyDirDefault is the location of the default keys to import const keyDirDefault = "/etc/mqm/pki/keys" -// keyDir is the location of the HA keys to import +// keyDirHA is the location of the HA keys to import const keyDirHA = "/etc/mqm/ha/pki/keys" -// trustDir is the location of the trust certificates to import +// trustDirDefault is the location of the trust certificates to import const trustDirDefault = "/etc/mqm/pki/trust" type KeyStoreData struct { @@ -103,7 +103,7 @@ func ConfigureDefaultTLSKeystores() (string, KeyStoreData, KeyStoreData, error) // ConfigureHATLSKeystore configures the CMS Keystore & PKCS#12 Truststore func ConfigureHATLSKeystore() (string, KeyStoreData, error) { - // Create the CMS Keystore & PKCS#12 Truststore + // Create a CMS Keystore only tlsStore, err := generateHAKeystore() if err != nil { return "", tlsStore.Keystore, err diff --git a/internal/tls/tls_web.go b/internal/tls/tls_web.go index 374aa64..2b84af8 100644 --- a/internal/tls/tls_web.go +++ b/internal/tls/tls_web.go @@ -1,5 +1,5 @@ /* -© Copyright IBM Corporation 2019, 2020 +© Copyright IBM Corporation 2019, 2021 Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/test/docker/docker_api_test_util.go b/test/docker/docker_api_test_util.go index 597d155..35a27b9 100644 --- a/test/docker/docker_api_test_util.go +++ b/test/docker/docker_api_test_util.go @@ -1,5 +1,5 @@ /* -© Copyright IBM Corporation 2017, 2020 +© Copyright IBM Corporation 2017, 2021 Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -313,6 +313,31 @@ func runContainerWithHostConfig(t *testing.T, cli *client.Client, containerConfi return ctr.ID } +// runContainerWithAllConfig creates and starts a container, using the supplied ContainerConfig, HostConfig, +// NetworkingConfig, and container name (or the value of t.Name if containerName=""). +func runContainerWithAllConfig(t *testing.T, cli *client.Client, containerConfig *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, containerName string) string { + if containerName == "" { + containerName = t.Name() + } + if containerConfig.Image == "" { + containerConfig.Image = imageName() + } + // Always run as a random user, unless the test has specified otherwise + if containerConfig.User == "" { + containerConfig.User = generateRandomUID() + } + // if coverage + containerConfig.Env = append(containerConfig.Env, "COVERAGE_FILE="+t.Name()+".cov") + containerConfig.Env = append(containerConfig.Env, "EXIT_CODE_FILE="+getExitCodeFilename(t)) + t.Logf("Running container (%s)", containerConfig.Image) + ctr, err := cli.ContainerCreate(context.Background(), containerConfig, hostConfig, networkingConfig, containerName) + if err != nil { + t.Fatal(err) + } + startContainer(t, cli, ctr.ID) + return ctr.ID +} + // runContainerWithPorts creates and starts a container, exposing the specified ports on the host. // If no image is specified in the container config, then the image name is retrieved from the TEST_IMAGE // environment variable. @@ -622,6 +647,9 @@ func waitForReady(t *testing.T, cli *client.Client, ID string) { } else if rc == 10 { t.Log("MQ Readiness: Queue Manager Running as Standby") return + } else if rc == 20 { + t.Log("MQ Readiness: Queue Manager Running as Replica") + return } case <-ctx.Done(): t.Fatal("Timed out waiting for container to become ready") @@ -836,3 +864,12 @@ func countTarLines(t *testing.T, b []byte) int { } return total } + +func getMQVersion(t *testing.T, cli *client.Client) (string, error) { + inspect, _, err := cli.ImageInspectWithRaw(context.Background(), imageName()) + if err != nil { + return "", err + } + version := inspect.ContainerConfig.Labels["version"] + return version, nil +} diff --git a/test/docker/go.mod b/test/docker/go.mod index 29c48f7..2247292 100644 --- a/test/docker/go.mod +++ b/test/docker/go.mod @@ -14,6 +14,7 @@ require ( github.com/onsi/gomega v1.10.2 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/pkg/errors v0.9.1 // indirect + github.com/spf13/cobra v1.1.1 // indirect gopkg.in/airbrake/gobrake.v2 v2.0.9 // indirect gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2 // indirect ) diff --git a/test/docker/go.sum b/test/docker/go.sum index c4ba356..ba21f48 100644 --- a/test/docker/go.sum +++ b/test/docker/go.sum @@ -1,23 +1,76 @@ +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/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= +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/Microsoft/go-winio v0.4.14 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+qxleU= github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/Sirupsen/logrus v1.0.5 h1:447dy9LxSj+Iaa2uN3yoFHOzU9yJcJYiQPtNz8OXtv0= github.com/Sirupsen/logrus v1.0.5/go.mod h1:rmk17hk6i8ZSAJkSDa7nOxamrG+SP4P0mm+DAvExv4U= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/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/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug= github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v1.13.2-0.20170601211448-f5ec1e2936dc h1:y4nIGNQUH6JtUV3pd6HjnzdnHq+96wMDVXhkfZ6jc4E= github.com/docker/docker v1.13.2-0.20170601211448-f5ec1e2936dc/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v20.10.2+incompatible h1:vFgEHPqWBTp4pTjdLwjAA4bSo3gvIGOYwuJTlEjVBCw= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +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-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/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/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/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= @@ -25,15 +78,78 @@ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:W github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/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 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +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/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/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= +github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/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/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.14.1 h1:jMU0WaQrP0a/YAEq8eJmJKjBoMs+pClEr1vDMlM/Do4= @@ -44,27 +160,124 @@ github.com/onsi/gomega v1.10.2 h1:aY/nuoWlKJud2J6U0E3NWsjlg+0GtwXxgEqthRdzlcs= github.com/onsi/gomega v1.10.2/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +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 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= +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/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1 h1:GL2rEmy6nsikmW0r8opw9JIRScdMF5hA8cOYLH7In1k= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v1.1.1 h1:KfztREH0tPxJJ+geloSLaAkaPkr4ki2Er5quFV1TDo4= +github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= +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 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= 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/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= 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/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/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/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/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-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-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7 h1:AeiKBIuRw3UomYXSbLy0Mc2dDLfdtbT/IVn4keq83P0= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +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/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/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +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-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-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-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b h1:ag/x1USPSsqHud38I9BAC88qdNLDHHtQ4mlgQIZPPNA= 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-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -72,11 +285,52 @@ golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200519105757-fe76b779f299 h1:DYfZAGf2WMFjMxbgTjaC+2HC7NkNAQs+6Q8b9WEB/F4= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 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 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +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/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-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-20190328211700-ab21143f2384/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-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-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/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/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/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/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/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= @@ -86,13 +340,26 @@ google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyz google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= gopkg.in/airbrake/gobrake.v2 v2.0.9 h1:7z2uVWwn7oVeeugY1DtlPAy5H+KYgB1KeKTnqjNatLo= gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 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/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2 h1:OAj3g0cR6Dx/R07QgQe8wkA9RNjB2u4i700xBkIT4e0= gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= +gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +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.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= diff --git a/test/docker/mq_native_ha_test.go b/test/docker/mq_native_ha_test.go new file mode 100644 index 0000000..564df3d --- /dev/null +++ b/test/docker/mq_native_ha_test.go @@ -0,0 +1,219 @@ +/* +© Copyright IBM Corporation 2021 + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package main + +import ( + "testing" + + "github.com/docker/docker/client" +) + +// TestNativeHABasic creates 3 containers in a Native HA queue manager configuration +// and ensures the queue manger and replicas start as expected +func TestNativeHABasic(t *testing.T) { + cli, err := client.NewEnvClient() + if err != nil { + t.Fatal(err) + } + + version, err := getMQVersion(t, cli) + if err != nil { + t.Fatal(err) + } + if version < "9.2.2.0" { + t.Skipf("Skipping %s as test requires at least MQ 9.2.2.0, but image is version %s", t.Name(), version) + } + + containerNames := [3]string{"QM1_1", "QM1_2", "QM1_3"} + qmReplicaIDs := [3]string{} + qmVolumes := []string{} + qmNetwork, err := createBridgeNetwork(cli, t) + if err != nil { + t.Fatal(err) + } + defer removeBridgeNetwork(cli, qmNetwork.ID) + + for i := 0; i <= 2; i++ { + vol := createVolume(t, cli, containerNames[i]) + defer removeVolume(t, cli, vol.Name) + qmVolumes = append(qmVolumes, vol.Name) + + containerConfig := getNativeHAContainerConfig(containerNames[i], containerNames, defaultHAPort) + hostConfig := getHostConfig(t, 1, "", "", vol.Name) + networkingConfig := getNativeHANetworkConfig(qmNetwork.ID) + + ctr := runContainerWithAllConfig(t, cli, &containerConfig, &hostConfig, &networkingConfig, containerNames[i]) + defer cleanContainer(t, cli, ctr) + qmReplicaIDs[i] = ctr + } + + waitForReadyHA(t, cli, qmReplicaIDs) + + _, err = getActiveReplicaInstances(t, cli, qmReplicaIDs) + if err != nil { + t.Fatal(err) + } + +} + +// TestNativeHAFailover creates 3 containers in a Native HA queue manager configuration, +// stops the active queue manager, checks a replica becomes active, and ensures the stopped +// queue manager comes back as a replica +func TestNativeHAFailover(t *testing.T) { + + cli, err := client.NewEnvClient() + if err != nil { + t.Fatal(err) + } + + version, err := getMQVersion(t, cli) + if err != nil { + t.Fatal(err) + } + if version < "9.2.2.0" { + t.Skipf("Skipping %s as test requires at least MQ 9.2.2.0, but image is version %s", t.Name(), version) + } + + containerNames := [3]string{"QM1_1", "QM1_2", "QM1_3"} + qmReplicaIDs := [3]string{} + qmVolumes := []string{} + qmNetwork, err := createBridgeNetwork(cli, t) + if err != nil { + t.Fatal(err) + } + defer removeBridgeNetwork(cli, qmNetwork.ID) + + for i := 0; i <= 2; i++ { + vol := createVolume(t, cli, containerNames[i]) + defer removeVolume(t, cli, vol.Name) + qmVolumes = append(qmVolumes, vol.Name) + + containerConfig := getNativeHAContainerConfig(containerNames[i], containerNames, defaultHAPort) + hostConfig := getHostConfig(t, 1, "", "", vol.Name) + networkingConfig := getNativeHANetworkConfig(qmNetwork.ID) + + ctr := runContainerWithAllConfig(t, cli, &containerConfig, &hostConfig, &networkingConfig, containerNames[i]) + defer cleanContainer(t, cli, ctr) + qmReplicaIDs[i] = ctr + } + + waitForReadyHA(t, cli, qmReplicaIDs) + + haStatus, err := getActiveReplicaInstances(t, cli, qmReplicaIDs) + if err != nil { + t.Fatal(err) + } + + stopContainer(t, cli, haStatus.Active) + waitForFailoverHA(t, cli, haStatus.Replica) + startContainer(t, cli, haStatus.Active) + waitForReady(t, cli, haStatus.Active) + + _, err = getActiveReplicaInstances(t, cli, qmReplicaIDs) + if err != nil { + t.Fatal(err) + } + +} + +// TestNativeHASecure creates 3 containers in a Native HA queue manager configuration +// with HA TLS enabled, and ensures the queue manger and replicas start as expected +func TestNativeHASecure(t *testing.T) { + cli, err := client.NewEnvClient() + if err != nil { + t.Fatal(err) + } + + version, err := getMQVersion(t, cli) + if err != nil { + t.Fatal(err) + } + if version < "9.2.2.0" { + t.Skipf("Skipping %s as test requires at least MQ 9.2.2.0, but image is version %s", t.Name(), version) + } + + containerNames := [3]string{"QM1_1", "QM1_2", "QM1_3"} + qmReplicaIDs := [3]string{} + qmNetwork, err := createBridgeNetwork(cli, t) + if err != nil { + t.Fatal(err) + } + defer removeBridgeNetwork(cli, qmNetwork.ID) + + for i := 0; i <= 2; i++ { + containerConfig := getNativeHAContainerConfig(containerNames[i], containerNames, defaultHAPort) + containerConfig.Env = append(containerConfig.Env, "MQ_NATIVE_HA_TLS=true") + hostConfig := getNativeHASecureHostConfig(t) + networkingConfig := getNativeHANetworkConfig(qmNetwork.ID) + + ctr := runContainerWithAllConfig(t, cli, &containerConfig, &hostConfig, &networkingConfig, containerNames[i]) + defer cleanContainer(t, cli, ctr) + qmReplicaIDs[i] = ctr + } + + waitForReadyHA(t, cli, qmReplicaIDs) + + _, err = getActiveReplicaInstances(t, cli, qmReplicaIDs) + if err != nil { + t.Fatal(err) + } + +} + +// TestNativeHASecure creates 3 containers in a Native HA queue manager configuration +// with HA TLS enabled, overrides the default CipherSpec, and ensures the queue manger +// and replicas start as expected +func TestNativeHASecureCipherSpec(t *testing.T) { + cli, err := client.NewEnvClient() + if err != nil { + t.Fatal(err) + } + + version, err := getMQVersion(t, cli) + if err != nil { + t.Fatal(err) + } + if version < "9.2.2.0" { + t.Skipf("Skipping %s as test requires at least MQ 9.2.2.0, but image is version %s", t.Name(), version) + } + + containerNames := [3]string{"QM1_1", "QM1_2", "QM1_3"} + qmReplicaIDs := [3]string{} + qmNetwork, err := createBridgeNetwork(cli, t) + if err != nil { + t.Fatal(err) + } + defer removeBridgeNetwork(cli, qmNetwork.ID) + + for i := 0; i <= 2; i++ { + containerConfig := getNativeHAContainerConfig(containerNames[i], containerNames, defaultHAPort) + containerConfig.Env = append(containerConfig.Env, "MQ_NATIVE_HA_TLS=true", "MQ_NATIVE_HA_CIPHERSPEC=TLS_AES_256_GCM_SHA384") + hostConfig := getNativeHASecureHostConfig(t) + networkingConfig := getNativeHANetworkConfig(qmNetwork.ID) + + ctr := runContainerWithAllConfig(t, cli, &containerConfig, &hostConfig, &networkingConfig, containerNames[i]) + defer cleanContainer(t, cli, ctr) + qmReplicaIDs[i] = ctr + } + + waitForReadyHA(t, cli, qmReplicaIDs) + + _, err = getActiveReplicaInstances(t, cli, qmReplicaIDs) + if err != nil { + t.Fatal(err) + } + +} diff --git a/test/docker/mq_native_ha_test_util.go b/test/docker/mq_native_ha_test_util.go new file mode 100644 index 0000000..c0cdf3c --- /dev/null +++ b/test/docker/mq_native_ha_test_util.go @@ -0,0 +1,145 @@ +/* +© Copyright IBM Corporation 2021 + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package main + +import ( + "context" + "fmt" + "path/filepath" + "testing" + "time" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/container" + "github.com/docker/docker/api/types/network" + "github.com/docker/docker/client" +) + +const defaultHAPort = 9414 + +// HAReplicaStatus represents the Active/Replica/Replica container status of the queue manager +type HAReplicaStatus struct { + Active string + Replica [2]string +} + +func createBridgeNetwork(cli *client.Client, t *testing.T) (types.NetworkCreateResponse, error) { + return cli.NetworkCreate(context.Background(), t.Name(), types.NetworkCreate{}) +} + +func removeBridgeNetwork(cli *client.Client, networkID string) error { + return cli.NetworkRemove(context.Background(), networkID) +} + +func getNativeHAContainerConfig(containerName string, replicaNames [3]string, haPort int) container.Config { + return container.Config{ + Env: []string{ + "LICENSE=accept", + "MQ_QMGR_NAME=QM1", + "AMQ_CLOUD_PAK=true", + "MQ_NATIVE_HA=true", + fmt.Sprintf("HOSTNAME=%s", containerName), + fmt.Sprintf("MQ_NATIVE_HA_INSTANCE_0_NAME=%s", replicaNames[0]), + fmt.Sprintf("MQ_NATIVE_HA_INSTANCE_1_NAME=%s", replicaNames[1]), + fmt.Sprintf("MQ_NATIVE_HA_INSTANCE_2_NAME=%s", replicaNames[2]), + fmt.Sprintf("MQ_NATIVE_HA_INSTANCE_0_REPLICATION_ADDRESS=%s(%d)", replicaNames[0], haPort), + fmt.Sprintf("MQ_NATIVE_HA_INSTANCE_1_REPLICATION_ADDRESS=%s(%d)", replicaNames[1], haPort), + fmt.Sprintf("MQ_NATIVE_HA_INSTANCE_2_REPLICATION_ADDRESS=%s(%d)", replicaNames[2], haPort), + }, + } +} + +func getNativeHASecureHostConfig(t *testing.T) container.HostConfig { + return container.HostConfig{ + Binds: []string{ + coverageBind(t), + filepath.Join(getCwd(t, true), "../tls") + ":/etc/mqm/ha/pki/keys/ha", + }, + } +} + +func getNativeHANetworkConfig(networkID string) network.NetworkingConfig { + return network.NetworkingConfig{ + EndpointsConfig: map[string]*network.EndpointSettings{ + networkID: &network.EndpointSettings{}, + }, + } +} + +func getActiveReplicaInstances(t *testing.T, cli *client.Client, qmReplicaIDs [3]string) (HAReplicaStatus, error) { + + var actives []string + var replicas []string + + for _, id := range qmReplicaIDs { + qmReplicaStatus := getQueueManagerStatus(t, cli, id, "QM1") + if qmReplicaStatus == "Running" { + actives = append(actives, id) + } else if qmReplicaStatus == "Replica" { + replicas = append(replicas, id) + } else { + err := fmt.Errorf("Expected status to be Running or Replica, got status: %s", qmReplicaStatus) + return HAReplicaStatus{}, err + } + } + + if len(actives) != 1 || len(replicas) != 2 { + err := fmt.Errorf("Expected 1 Active and 2 Replicas, got: %d Active and %d Replica", len(actives), len(replicas)) + return HAReplicaStatus{}, err + } + + return HAReplicaStatus{actives[0], [2]string{replicas[0], replicas[1]}}, nil +} + +func waitForReadyHA(t *testing.T, cli *client.Client, qmReplicaIDs [3]string) { + + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) + defer cancel() + + for { + select { + case <-time.After(1 * time.Second): + for _, id := range qmReplicaIDs { + rc, _ := execContainer(t, cli, id, "", []string{"chkmqready"}) + if rc == 0 { + t.Log("MQ is ready") + return + } + } + case <-ctx.Done(): + t.Fatal("Timed out waiting for HA Queue Manager to become ready") + } + } +} + +func waitForFailoverHA(t *testing.T, cli *client.Client, replicas [2]string) { + + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) + defer cancel() + + for { + select { + case <-time.After(1 * time.Second): + for _, id := range replicas { + if status := getQueueManagerStatus(t, cli, id, "QM1"); status == "Running" { + return + } + } + case <-ctx.Done(): + t.Fatal("Timed out waiting for Native HA Queue Manager to failover to an available replica") + } + } +} From 2ae82d71d68347116a88b23ea798e8c3f8c392bc Mon Sep 17 00:00:00 2001 From: Stephen D Marshall Date: Thu, 21 Jan 2021 11:13:08 +0000 Subject: [PATCH 7/8] Chkmqstarted (#135) * Add chkmqstarted * Fix go.sum * Fix bug & extra unit-test check * Clean-up StartupProbe output --- Dockerfile-server | 2 + cmd/chkmqstarted/main.go | 72 +++++++++++++++++++++++++++ docs/internals.md | 1 + test/docker/docker_api_test.go | 41 +++++++++++++++ test/docker/go.sum | 1 + test/docker/mq_native_ha_test_util.go | 6 ++- 6 files changed, 122 insertions(+), 1 deletion(-) create mode 100644 cmd/chkmqstarted/main.go diff --git a/Dockerfile-server b/Dockerfile-server index 4225dd1..892f539 100644 --- a/Dockerfile-server +++ b/Dockerfile-server @@ -45,12 +45,14 @@ ENV PATH="${PATH}:/opt/mqm/bin" RUN go build -ldflags "-X \"main.ImageCreated=$(date --iso-8601=seconds)\" -X \"main.ImageRevision=$IMAGE_REVISION\" -X \"main.ImageSource=$IMAGE_SOURCE\" -X \"main.ImageTag=$IMAGE_TAG\"" ./cmd/runmqserver/ RUN go build ./cmd/chkmqready/ RUN go build ./cmd/chkmqhealthy/ +RUN go build ./cmd/chkmqstarted/ RUN go build ./cmd/runmqdevserver/ RUN go build -buildmode=c-shared -o amqpasdev.so ./internal/qmgrauth/pas.go RUN go test -v ./cmd/runmqdevserver/... RUN go test -v ./cmd/runmqserver/ RUN go test -v ./cmd/chkmqready/ RUN go test -v ./cmd/chkmqhealthy/ +RUN go test -v ./cmd/chkmqstarted/ RUN go test -v ./pkg/... RUN go test -v ./internal/... RUN go vet ./cmd/... ./internal/... diff --git a/cmd/chkmqstarted/main.go b/cmd/chkmqstarted/main.go new file mode 100644 index 0000000..d9964a9 --- /dev/null +++ b/cmd/chkmqstarted/main.go @@ -0,0 +1,72 @@ +/* +© Copyright IBM Corporation 2021 + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// chkmqstarted checks that MQ has successfully started, by checking the output of the "dspmq" command +package main + +import ( + "fmt" + "os" + "os/exec" + "strings" + + "github.com/ibm-messaging/mq-container/pkg/name" +) + +func queueManagerStarted() (bool, error) { + name, err := name.GetQueueManagerName() + if err != nil { + return false, err + } + // Specify the queue manager name, just in case someone's created a second queue manager + // #nosec G204 + cmd := exec.Command("dspmq", "-n", "-m", name) + // Run the command and wait for completion + out, err := cmd.CombinedOutput() + if err != nil { + fmt.Println(err) + return false, err + } + if !strings.Contains(string(out), "(RUNNING)") && !strings.Contains(string(out), "(RUNNING AS STANDBY)") && !strings.Contains(string(out), "(STARTING)") && !strings.Contains(string(out), "(REPLICA)") { + return false, nil + } + if os.Getenv("MQ_NATIVE_HA") == "true" { + // Specify the queue manager name, just in case someone's created a second queue manager + // #nosec G204 + cmd = exec.Command("dspmq", "-o", "nativeha", "-m", name) + // Run the command and wait for completion + out, err = cmd.CombinedOutput() + if err != nil { + fmt.Println(err) + return false, err + } + if !strings.Contains(string(out), "INSYNC(yes)") { + return false, nil + } + } + return true, nil +} + +func main() { + started, err := queueManagerStarted() + if err != nil { + os.Exit(2) + } + if !started { + os.Exit(1) + } + os.Exit(0) +} diff --git a/docs/internals.md b/docs/internals.md index 3327b28..b2a0343 100644 --- a/docs/internals.md +++ b/docs/internals.md @@ -11,6 +11,7 @@ The resulting Docker image contains the following: - `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: diff --git a/test/docker/docker_api_test.go b/test/docker/docker_api_test.go index 3c395fd..7ffd8f7 100644 --- a/test/docker/docker_api_test.go +++ b/test/docker/docker_api_test.go @@ -1475,3 +1475,44 @@ func TestHealthCheckWithNoNewPrivileges(t *testing.T) { func TestHealthCheckWithNewPrivileges(t *testing.T) { utilTestHealthCheck(t, false) } + +// utilTestStartedCheck is used by TestStartedCheck* to run a container with +// privileges enabled or disabled. Otherwise the same as the golden path tests. +func utilTestStartedCheck(t *testing.T, nonewpriv bool) { + t.Parallel() + cli, err := client.NewEnvClient() + if err != nil { + t.Fatal(err) + } + containerConfig := container.Config{ + Env: []string{"LICENSE=accept", "MQ_QMGR_NAME=qm1"}, + } + hostConfig := getDefaultHostConfig(t, cli) + hostConfig.SecurityOpt = append(hostConfig.SecurityOpt, fmt.Sprintf("no-new-privileges:%v", nonewpriv)) + id := runContainerWithHostConfig(t, cli, &containerConfig, hostConfig) + defer cleanContainer(t, cli, id) + waitForReady(t, cli, id) + rc, out := execContainer(t, cli, id, "", []string{"chkmqstarted"}) + t.Log(out) + if rc != 0 { + t.Errorf("Expected chkmqstarted to return with exit code 0; got \"%v\"", rc) + t.Logf("Output from chkmqstarted:\n%v", out) + } + // Stop the container cleanly + stopContainer(t, cli, id) +} + +// TestStartedCheckWithNoNewPrivileges tests golden path start/stop plus +// chkmqstarted, when running in a container where no new privileges are +// allowed (i.e. setuid is disabled) +func TestStartedCheckWithNoNewPrivileges(t *testing.T) { + utilTestStartedCheck(t, true) +} + +// TestStartedCheckWithNoNewPrivileges tests golden path start/stop plus +// chkmqstarted when running in a container where new privileges are +// allowed (i.e. setuid is allowed) +// See https://github.com/ibm-messaging/mq-container/issues/428 +func TestStartedCheckWithNewPrivileges(t *testing.T) { + utilTestStartedCheck(t, false) +} diff --git a/test/docker/go.sum b/test/docker/go.sum index ba21f48..ffc2ea8 100644 --- a/test/docker/go.sum +++ b/test/docker/go.sum @@ -219,6 +219,7 @@ golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= 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 h1:58fnuSXlxZmFdJyvtTFVmVhcMLU6v5fEb/ok4wyqtNU= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 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= diff --git a/test/docker/mq_native_ha_test_util.go b/test/docker/mq_native_ha_test_util.go index c0cdf3c..a94c68d 100644 --- a/test/docker/mq_native_ha_test_util.go +++ b/test/docker/mq_native_ha_test_util.go @@ -116,7 +116,11 @@ func waitForReadyHA(t *testing.T, cli *client.Client, qmReplicaIDs [3]string) { rc, _ := execContainer(t, cli, id, "", []string{"chkmqready"}) if rc == 0 { t.Log("MQ is ready") - return + rc, _ := execContainer(t, cli, id, "", []string{"chkmqstarted"}) + if rc == 0 { + t.Log("MQ has started") + return + } } } case <-ctx.Done(): From b7dcff0bbcd815f4b2a73b709f8b6cc2943f5095 Mon Sep 17 00:00:00 2001 From: Luke Powlett Date: Tue, 2 Feb 2021 17:38:55 +0000 Subject: [PATCH 8/8] Set main branch back to private-master for merge --- .travis.yml | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0defd61..f78ff69 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,7 +25,7 @@ services: env: global: - - MAIN_BRANCH=native-ha + - MAIN_BRANCH=private-master - MQ_LTS_VERSION=9.2.0.1 - TAGCACHE_FILE=tagcache - RELEASE=r1 @@ -40,7 +40,7 @@ go_import_path: "github.com/ibm-messaging/mq-container" jobs: include: - stage: basic-build - if: branch != native-ha AND tag IS blank + if: branch != private-master AND tag IS blank name: "Basic AMD64 build" os: linux env: @@ -50,12 +50,12 @@ jobs: # CD Build - stage: global-tag - if: branch = native-ha AND type != pull_request OR tag =~ ^release-candidate* + if: branch = private-master AND type != pull_request OR tag =~ ^release-candidate* name: "Generate Global Tag" os: linux script: bash -e travis-build-scripts/global-tag.sh - stage: build - if: branch = native-ha OR tag =~ ^release-candidate* + if: branch = private-master OR tag =~ ^release-candidate* name: "Multi-Arch AMD64 build" os: linux env: @@ -63,7 +63,7 @@ jobs: - MQ_ARCHIVE_REPOSITORY=$MQ_922_ARCHIVE_REPOSITORY_AMD64 - MQ_ARCHIVE_REPOSITORY_DEV=$MQ_922_ARCHIVE_REPOSITORY_DEV_AMD64 script: bash -e travis-build-scripts/run.sh - # - if: branch = native-ha OR tag =~ ^release-candidate* + # - if: branch = private-master OR tag =~ ^release-candidate* # name: "Multi-Arch PPC64LE build" # os: linux-ppc64le # env: @@ -73,7 +73,7 @@ jobs: # - MQ_ARCHIVE_REPOSITORY_DEV=$MQ_922_ARCHIVE_REPOSITORY_DEV_PPC64LE # script: bash -e travis-build-scripts/run.sh - stage: build - if: branch = native-ha OR tag =~ ^release-candidate* + if: branch = private-master OR tag =~ ^release-candidate* name: "Multi-Arch S390X build" os: linux-s390 env: @@ -83,7 +83,7 @@ jobs: - MQ_ARCHIVE_REPOSITORY_DEV=$MQ_922_ARCHIVE_REPOSITORY_DEV_S390X script: bash -e travis-build-scripts/run.sh - stage: push-manifest - if: branch = native-ha AND type != pull_request OR tag =~ ^release-candidate* + if: branch = private-master AND type != pull_request OR tag =~ ^release-candidate* name: "Push Manifest-list to registry" env: - PUSH_MANIFEST_ONLY=true @@ -92,7 +92,7 @@ jobs: # LTS Build - stage: global-tag - if: branch = native-ha AND type != pull_request OR tag =~ ^release-candidate* + if: branch = private-master AND type != pull_request OR tag =~ ^release-candidate* name: "Generate Global Tag" os: linux env: @@ -102,7 +102,7 @@ jobs: - RELEASE=$RELEASE_LTS script: bash -e travis-build-scripts/global-tag.sh - stage: build - if: branch = native-ha OR tag =~ ^release-candidate* + if: branch = private-master OR tag =~ ^release-candidate* name: "Multi-Arch AMD64 build" os: linux env: @@ -113,7 +113,7 @@ jobs: - RELEASE=$RELEASE_LTS script: bash -e travis-build-scripts/run.sh - stage: build - if: branch = native-ha OR tag =~ ^release-candidate* + if: branch = private-master OR tag =~ ^release-candidate* name: "Multi-Arch S390X build" os: linux-s390 env: @@ -125,7 +125,7 @@ jobs: - RELEASE=$RELEASE_LTS script: bash -e travis-build-scripts/run.sh - stage: push-manifest - if: branch = native-ha AND type != pull_request OR tag =~ ^release-candidate* + if: branch = private-master AND type != pull_request OR tag =~ ^release-candidate* name: "Push Manifest-list to registry" env: - LTS=true