From 911f9db2fd2d44c3c200d2b182df53a4f70dfea1 Mon Sep 17 00:00:00 2001 From: Arthur Barr Date: Wed, 14 Feb 2018 11:18:20 +0000 Subject: [PATCH] Update readiness check --- cmd/chkmqready/main.go | 10 ++++- cmd/runmqserver/main.go | 26 ++++++++++--- internal/ready/ready.go | 70 ++++++++++++++++++++++++++++++++++ test/docker/docker_api_test.go | 55 +++++++++++++++++++++++++- 4 files changed, 152 insertions(+), 9 deletions(-) create mode 100644 internal/ready/ready.go diff --git a/cmd/chkmqready/main.go b/cmd/chkmqready/main.go index 21da818..8e6e4b1 100644 --- a/cmd/chkmqready/main.go +++ b/cmd/chkmqready/main.go @@ -1,5 +1,5 @@ /* -© Copyright IBM Corporation 2017 +© Copyright IBM Corporation 2017, 2018 Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -20,9 +20,17 @@ package main import ( "net" "os" + + "github.com/ibm-messaging/mq-container/internal/ready" ) func main() { + // Check if runmqserver has indicated that it's finished configuration + r, err := ready.Check() + if !r || err != nil { + os.Exit(1) + } + // Check if the queue manager has a running listener conn, err := net.Dial("tcp", "127.0.0.1:1414") if err != nil { os.Exit(1) diff --git a/cmd/runmqserver/main.go b/cmd/runmqserver/main.go index c2dc047..8f34cea 100644 --- a/cmd/runmqserver/main.go +++ b/cmd/runmqserver/main.go @@ -20,6 +20,7 @@ package main import ( "errors" "fmt" + "io" "io/ioutil" "os" "os/exec" @@ -31,6 +32,7 @@ import ( "github.com/ibm-messaging/mq-container/internal/command" "github.com/ibm-messaging/mq-container/internal/name" + "github.com/ibm-messaging/mq-container/internal/ready" ) var debug = false @@ -108,18 +110,23 @@ func configureQueueManager() error { for _, file := range files { if strings.HasSuffix(file.Name(), ".mqsc") { abs := filepath.Join(configDir, file.Name()) - mqsc, err := ioutil.ReadFile(abs) - if err != nil { - log.Println(err) - return err - } cmd := exec.Command("runmqsc") stdin, err := cmd.StdinPipe() if err != nil { log.Println(err) return err } - stdin.Write(mqsc) + // Open the MQSC file for reading + f, err := os.Open(abs) + if err != nil { + log.Printf("Error opening %v: %v", abs, err) + } + // Copy the contents to stdin of the runmqsc process + _, err = io.Copy(stdin, f) + if err != nil { + log.Printf("Error reading %v: %v", abs, err) + } + f.Close() stdin.Close() // Run the command and wait for completion out, err := cmd.CombinedOutput() @@ -196,6 +203,10 @@ func configureLogger() { func doMain() error { configureLogger() + err := ready.Clear() + if err != nil { + return err + } debugEnv, ok := os.LookupEnv("DEBUG") if ok && (debugEnv == "true" || debugEnv == "1") { debug = true @@ -271,6 +282,9 @@ func doMain() error { // Reap zombies now, just in case we've already got some signalControl <- reapNow + // Write a file to indicate that chkmqready should now work as normal + ready.Set() + // Wait for terminate signal <-signalControl if mirrorLogs() { diff --git a/internal/ready/ready.go b/internal/ready/ready.go new file mode 100644 index 0000000..80a2c61 --- /dev/null +++ b/internal/ready/ready.go @@ -0,0 +1,70 @@ +/* +© Copyright IBM Corporation 2018 + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package ready contains code to provide a ready signal mechanism between processes +package ready + +import ( + "io/ioutil" + "os" + + log "github.com/sirupsen/logrus" +) + +const fileName string = "/tmp/runmqserverReady" + +func fileExists() (bool, error) { + _, err := os.Stat(fileName) + if err != nil { + if !os.IsNotExist(err) { + return false, err + } + return false, nil + } + return true, nil +} + +// Clear ensures that any readiness state is cleared +func Clear() error { + log.Debug("Clear()") + exist, err := fileExists() + if err != nil { + return err + } + if exist { + return os.Remove(fileName) + } + return nil +} + +// Set lets any subsequent calls to `CheckReady` know that the queue +// manager has finished its configuration step +func Set() error { + log.Debug("Set()") + return ioutil.WriteFile(fileName, []byte("1"), 0770) +} + +// Check checks whether or not the queue manager has finished its +// configuration steps +func Check() (bool, error) { + exists, err := fileExists() + if err != nil { + log.Debug("Check() -> false") + return false, err + } + log.Debugf("Check() -> %v", exists) + return exists, nil +} diff --git a/test/docker/docker_api_test.go b/test/docker/docker_api_test.go index 7be1e4e..9406fe4 100644 --- a/test/docker/docker_api_test.go +++ b/test/docker/docker_api_test.go @@ -1,5 +1,5 @@ /* -© Copyright IBM Corporation 2017 +© Copyright IBM Corporation 2017, 2018 Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -349,7 +349,7 @@ func TestMQSC(t *testing.T) { var files = []struct { Name, Body string }{ - {"Dockerfile", fmt.Sprintf("FROM %v\nADD test.mqsc /etc/mqm/", imageName())}, + {"Dockerfile", fmt.Sprintf("FROM %v\nRUN rm -f /etc/mqm/*.mqsc\nADD test.mqsc /etc/mqm/", imageName())}, {"test.mqsc", "DEFINE QLOCAL(test)"}, } tag := createImage(t, cli, files) @@ -368,6 +368,57 @@ func TestMQSC(t *testing.T) { } } +// TestReadiness creates a new image with large amounts of MQSC in, to +// ensure that the readiness check doesn't pass until configuration has finished. +// WARNING: This test is sensitive to the speed of the machine it's running on. +func TestReadiness(t *testing.T) { + t.Parallel() + cli, err := client.NewEnvClient() + if err != nil { + t.Fatal(err) + } + const numQueues = 3 + var buf bytes.Buffer + // fmt.Fprintf(&b, "world!") + + // b := make([]byte, 32768) + // buf := bytes.NewBuffer(b) + for i := 1; i <= numQueues; i++ { + fmt.Fprintf(&buf, "* Defining queue test %v\nDEFINE QLOCAL(test%v)\n", i, i) + } + var files = []struct { + Name, Body string + }{ + {"Dockerfile", fmt.Sprintf("FROM %v\nRUN rm -f /etc/mqm/*.mqsc\nADD test.mqsc /etc/mqm/", imageName())}, + {"test.mqsc", buf.String()}, + } + tag := createImage(t, cli, files) + defer deleteImage(t, cli, tag) + + containerConfig := container.Config{ + Env: []string{"LICENSE=accept", "MQ_QMGR_NAME=qm1", "DEBUG=1"}, + Image: tag, + } + id := runContainer(t, cli, &containerConfig) + defer cleanContainer(t, cli, id) + queueCheckCommand := fmt.Sprintf("echo 'DISPLAY QLOCAL(test%v)' | runmqsc", numQueues) + t.Log(execContainerWithOutput(t, cli, id, "root", []string{"cat", "/etc/mqm/test.mqsc"})) + for { + readyRC := execContainerWithExitCode(t, cli, id, "mqm", []string{"chkmqready"}) + queueCheckRC := execContainerWithExitCode(t, cli, id, "mqm", []string{"bash", "-c", queueCheckCommand}) + t.Logf("readyRC=%v,queueCheckRC=%v\n", readyRC, queueCheckRC) + if readyRC == 0 { + if queueCheckRC != 0 { + t.Fatalf("chkmqready returned %v when MQSC had not finished", readyRC) + } else { + // chkmqready says OK, and the last queue exists, so return + t.Log(execContainerWithOutput(t, cli, id, "root", []string{"bash", "-c", "echo 'DISPLAY QLOCAL(test1)' | runmqsc"})) + return + } + } + } +} + func countLines(t *testing.T, r io.Reader) int { scanner := bufio.NewScanner(r) count := 0