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