From ed618dc6f6d8c2036b4e2eed058c75bf845c2513 Mon Sep 17 00:00:00 2001 From: Nicholas Daffern Date: Wed, 25 Jan 2023 10:33:24 +0000 Subject: [PATCH] Add logic to set custom Log File Page number (#371) * Add logic to set custom Log File Page number Signed-off-by: Nicholas-Daffern --- cmd/runmqserver/qmgr.go | 60 ++++++++++--- cmd/runmqserver/qmgr_test.go | 86 +++++++++++++++++++ .../test-files/testvalidateLogFilePages_1.ini | 9 ++ .../test-files/testvalidateLogFilePages_2.ini | 9 ++ .../test-files/testvalidateLogFilePages_3.ini | 9 ++ .../test-files/testvalidateLogFilePages_4.ini | 8 ++ .../test-files/testvalidateLogFilePages_5.ini | 8 ++ test/docker/docker_api_test.go | 77 +++++++++++------ test/docker/docker_api_test_util.go | 10 +++ 9 files changed, 238 insertions(+), 38 deletions(-) create mode 100644 cmd/runmqserver/qmgr_test.go create mode 100644 cmd/runmqserver/test-files/testvalidateLogFilePages_1.ini create mode 100644 cmd/runmqserver/test-files/testvalidateLogFilePages_2.ini create mode 100644 cmd/runmqserver/test-files/testvalidateLogFilePages_3.ini create mode 100644 cmd/runmqserver/test-files/testvalidateLogFilePages_4.ini create mode 100644 cmd/runmqserver/test-files/testvalidateLogFilePages_5.ini diff --git a/cmd/runmqserver/qmgr.go b/cmd/runmqserver/qmgr.go index 25e0782..23ed699 100644 --- a/cmd/runmqserver/qmgr.go +++ b/cmd/runmqserver/qmgr.go @@ -1,5 +1,5 @@ /* -© Copyright IBM Corporation 2017, 2022 +© Copyright IBM Corporation 2017, 2023 Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -55,23 +55,36 @@ func createDirStructure() error { func createQueueManager(name string, devMode bool) (bool, error) { log.Printf("Creating queue manager %v", name) - // Run 'dspmqinf' to check if 'mqs.ini' configuration file exists - // If command succeeds, the queue manager (or standby queue manager) has already been created - _, _, err := command.Run("dspmqinf", name) - if err == nil { - log.Printf("Detected existing queue manager %v", name) - return false, nil - } - mounts, err := containerruntime.GetMounts() if err != nil { log.Printf("Error getting mounts for queue manager") return false, err } + dataDir := getQueueManagerDataDir(mounts, name) + + // Run 'dspmqinf' to check if 'mqs.ini' configuration file exists + // If command succeeds, the queue manager (or standby queue manager) has already been created + _, _, err = command.Run("dspmqinf", name) + if err == nil { + log.Printf("Detected existing queue manager %v", name) + // Check if MQ_QMGR_LOG_FILE_PAGES matches the value set in qm.ini + lfp := os.Getenv("MQ_QMGR_LOG_FILE_PAGES") + if lfp != "" { + qmIniBytes, err := readQMIni(dataDir) + if err != nil { + log.Printf("Error reading qm.ini : %v", err) + return false, err + } + if !validateLogFilePageSetting(qmIniBytes, lfp) { + log.Println("Warning: the value of MQ_QMGR_LOG_FILE_PAGES does not match the value of 'LogFilePages' in the qm.ini. This setting cannot be altered after Queue Manager creation.") + } + } + return false, nil + } + // Check if 'qm.ini' configuration file exists for the queue manager // TODO : handle possible race condition - use a file lock? - dataDir := getQueueManagerDataDir(mounts, name) _, err = os.Stat(filepath.Join(dataDir, "qm.ini")) if err != nil { // If 'qm.ini' is not found - run 'crtmqm' to create a new queue manager @@ -96,6 +109,25 @@ func createQueueManager(name string, devMode bool) (bool, error) { return true, nil } +//readQMIni reads the qm.ini file and returns it as a byte array +//This function is specific to comply with the nosec. +func readQMIni(dataDir string) ([]byte, error) { + qmgrDir := filepath.Join(dataDir, "qm.ini") + // #nosec G304 - qmgrDir filepath is derived from dspmqinf + iniFileBytes, err := ioutil.ReadFile(qmgrDir) + if err != nil { + return nil, err + } + return iniFileBytes, err +} + +//validateLogFilePageSetting validates if the specified logFilePage number is equal to the existing value in the qm.ini +func validateLogFilePageSetting(iniFileBytes []byte, logFilePages string) bool { + lfpString := "LogFilePages=" + logFilePages + qminiConfigStr := string(iniFileBytes) + return strings.Contains(qminiConfigStr, lfpString) +} + func updateCommandLevel() error { level, ok := os.LookupEnv("MQ_CMDLEVEL") if ok && level != "" { @@ -238,6 +270,14 @@ func getCreateQueueManagerArgs(mounts map[string]string, name string, devMode bo if _, ok := mounts["/mnt/mqm-data"]; ok { args = append(args, "-md", "/mnt/mqm-data/qmgrs") } + if os.Getenv("MQ_QMGR_LOG_FILE_PAGES") != "" { + _, err = strconv.Atoi(os.Getenv("MQ_QMGR_LOG_FILE_PAGES")) + if err != nil { + log.Printf("Error processing MQ_QMGR_LOG_FILE_PAGES, the default value for LogFilePages will be used. Err: %v", err) + } else { + args = append(args, "-lf", os.Getenv("MQ_QMGR_LOG_FILE_PAGES")) + } + } args = append(args, name) return args } diff --git a/cmd/runmqserver/qmgr_test.go b/cmd/runmqserver/qmgr_test.go new file mode 100644 index 0000000..3c16fdc --- /dev/null +++ b/cmd/runmqserver/qmgr_test.go @@ -0,0 +1,86 @@ +/* +© Copyright IBM Corporation 2023 + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package main + +import ( + "io/ioutil" + "testing" +) + +func Test_validateLogFilePageSetting(t *testing.T) { + type args struct { + iniFilePath string + isValid bool + logFilePagesValue string + } + tests := []struct { + name string + args args + }{ + { + name: "TestLogFilePages1", + args: args{ + iniFilePath: "./test-files/testvalidateLogFilePages_1.ini", + isValid: true, + logFilePagesValue: "1235", + }, + }, + { + name: "TestLogFilePages2", + args: args{ + iniFilePath: "./test-files/testvalidateLogFilePages_2.ini", + isValid: true, + logFilePagesValue: "2224", + }, + }, + { + name: "TestLogFilePages3", + args: args{ + iniFilePath: "./test-files/testvalidateLogFilePages_3.ini", + isValid: false, + logFilePagesValue: "1235", + }, + }, + { + name: "TestLogFilePages4", + args: args{ + iniFilePath: "./test-files/testvalidateLogFilePages_4.ini", + isValid: false, + logFilePagesValue: "1235", + }, + }, + { + name: "TestLogFilePages5", + args: args{ + iniFilePath: "./test-files/testvalidateLogFilePages_5.ini", + isValid: false, + logFilePagesValue: "1235", + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + iniFileBytes, err := ioutil.ReadFile(tt.args.iniFilePath) + if err != nil { + t.Fatal(err) + } + validate := validateLogFilePageSetting(iniFileBytes, tt.args.logFilePagesValue) + if validate != tt.args.isValid { + t.Fatalf("Expected ini file validation output to be %v got %v", tt.args.isValid, validate) + } + }) + } +} diff --git a/cmd/runmqserver/test-files/testvalidateLogFilePages_1.ini b/cmd/runmqserver/test-files/testvalidateLogFilePages_1.ini new file mode 100644 index 0000000..eb9ec81 --- /dev/null +++ b/cmd/runmqserver/test-files/testvalidateLogFilePages_1.ini @@ -0,0 +1,9 @@ +ExitPath: + ExitsDefaultPath=/mnt/mqm/data/exits + ExitsDefaultPath64=/mnt/mqm/data/exits64 +Log: + LogPrimaryFiles=3 + LogSecondaryFiles=2 + LogFilePages=1235 + LogBufferPages=0 + LogWriteIntegrity=TripleWrite \ No newline at end of file diff --git a/cmd/runmqserver/test-files/testvalidateLogFilePages_2.ini b/cmd/runmqserver/test-files/testvalidateLogFilePages_2.ini new file mode 100644 index 0000000..1938f30 --- /dev/null +++ b/cmd/runmqserver/test-files/testvalidateLogFilePages_2.ini @@ -0,0 +1,9 @@ +ExitPath: + ExitsDefaultPath=/mnt/mqm/data/exits + ExitsDefaultPath64=/mnt/mqm/data/exits64 + Log: + LogPrimaryFiles=3 + LogSecondaryFiles=2 + LogFilePages=2224 + LogBufferPages=0 + LogWriteIntegrity=TripleWrite \ No newline at end of file diff --git a/cmd/runmqserver/test-files/testvalidateLogFilePages_3.ini b/cmd/runmqserver/test-files/testvalidateLogFilePages_3.ini new file mode 100644 index 0000000..6c368b7 --- /dev/null +++ b/cmd/runmqserver/test-files/testvalidateLogFilePages_3.ini @@ -0,0 +1,9 @@ +ExitPath: + ExitsDefaultPath=/mnt/mqm/data/exits + ExitsDefaultPath64=/mnt/mqm/data/exits64 +Log: + LogPrimaryFiles=3 + LogSecondaryFiles=2 + LogFilePages=6002 + LogBufferPages=0 + LogWriteIntegrity=TripleWrite \ No newline at end of file diff --git a/cmd/runmqserver/test-files/testvalidateLogFilePages_4.ini b/cmd/runmqserver/test-files/testvalidateLogFilePages_4.ini new file mode 100644 index 0000000..e9e3b5b --- /dev/null +++ b/cmd/runmqserver/test-files/testvalidateLogFilePages_4.ini @@ -0,0 +1,8 @@ +ExitPath: + ExitsDefaultPath=/mnt/mqm/data/exits + ExitsDefaultPath64=/mnt/mqm/data/exits64 +Log: + LogPrimaryFiles=3 + LogSecondaryFiles=2 + LogBufferPages=0 + LogWriteIntegrity=TripleWrite \ No newline at end of file diff --git a/cmd/runmqserver/test-files/testvalidateLogFilePages_5.ini b/cmd/runmqserver/test-files/testvalidateLogFilePages_5.ini new file mode 100644 index 0000000..41e4d91 --- /dev/null +++ b/cmd/runmqserver/test-files/testvalidateLogFilePages_5.ini @@ -0,0 +1,8 @@ +ExitPath: + ExitsDefaultPath=/mnt/mqm/data/exits + ExitsDefaultPath64=/mnt/mqm/data/exits64 +Log: + LogPrimaryFiles=3 + LogSecondaryFiles=2 + LogBufferPages=1235 + LogWriteIntegrity=TripleWrite \ No newline at end of file diff --git a/test/docker/docker_api_test.go b/test/docker/docker_api_test.go index 73aeb97..f490d63 100644 --- a/test/docker/docker_api_test.go +++ b/test/docker/docker_api_test.go @@ -1,5 +1,5 @@ /* -© Copyright IBM Corporation 2017, 2022 +© Copyright IBM Corporation 2017, 2023 Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -77,45 +77,20 @@ func TestLicenseView(t *testing.T) { } } -// Start a container with qm grace set to x seconds -// Check that when the container is stopped that the command endmqm has option -tp and x -func TestEndMQMOpts(t *testing.T) { - t.Parallel() - cli, err := client.NewClientWithOpts(client.FromEnv) - if err != nil { - t.Fatal(err) - } - containerConfig := container.Config{ - Env: []string{"LICENSE=accept", "MQ_GRACE_PERIOD=27"}, - } - - id := runContainer(t, cli, &containerConfig) - defer cleanContainer(t, cli, id) - waitForReady(t, cli, id) - killContainer(t, cli, id, "SIGTERM") - _, out := execContainer(t, cli, id, "", []string{"bash", "-c", "ps -ef | grep 'endmqm -w -r -tp 27'"}) - t.Log(out) - if !strings.Contains(out, "endmqm -w -r -tp 27") { - t.Errorf("Expected endmqm options endmqm -w -r -tp 27; got \"%v\"", out) - } -} - // TestGoldenPath starts a queue manager successfully when metrics are enabled func TestGoldenPathWithMetrics(t *testing.T) { t.Parallel() - goldenPath(t, true) } // TestGoldenPath starts a queue manager successfully when metrics are disabled func TestGoldenPathNoMetrics(t *testing.T) { t.Parallel() - goldenPath(t, false) } // Actual test function for TestGoldenPathNoMetrics & TestGoldenPathWithMetrics -func goldenPath(t *testing.T, metric bool) { +func goldenPath(t *testing.T, metrics bool) { cli, err := client.NewClientWithOpts(client.FromEnv) if err != nil { t.Fatal(err) @@ -123,13 +98,17 @@ func goldenPath(t *testing.T, metric bool) { containerConfig := container.Config{ Env: []string{"LICENSE=accept", "MQ_QMGR_NAME=qm1"}, } - if metric { + if metrics { containerConfig.Env = append(containerConfig.Env, "MQ_ENABLE_METRICS=true") } id := runContainer(t, cli, &containerConfig) defer cleanContainer(t, cli, id) waitForReady(t, cli, id) + + t.Run("Validate Default LogFilePages", func(t *testing.T) { + testLogFilePages(t, cli, id, "qm1", "4096") + }) // Stop the container cleanly stopContainer(t, cli, id) } @@ -1482,3 +1461,45 @@ func TestStartedCheckWithNoNewPrivileges(t *testing.T) { func TestStartedCheckWithNewPrivileges(t *testing.T) { utilTestStartedCheck(t, false) } + +// Start a container with qm grace set to x seconds +// Check that when the container is stopped that the command endmqm has option -tp and x +func TestEndMQMOpts(t *testing.T) { + t.Parallel() + cli, err := client.NewClientWithOpts(client.FromEnv) + if err != nil { + t.Fatal(err) + } + containerConfig := container.Config{ + Env: []string{"LICENSE=accept", "MQ_GRACE_PERIOD=27"}, + } + + id := runContainer(t, cli, &containerConfig) + defer cleanContainer(t, cli, id) + waitForReady(t, cli, id) + killContainer(t, cli, id, "SIGTERM") + _, out := execContainer(t, cli, id, "", []string{"bash", "-c", "ps -ef | grep 'endmqm -w -r -tp 27'"}) + t.Log(out) + if !strings.Contains(out, "endmqm -w -r -tp 27") { + t.Errorf("Expected endmqm options endmqm -w -r -tp 27; got \"%v\"", out) + } +} + +//TestCustomLogFilePages starts a qmgr with a custom number of logfilepages set. +//Check that the number of logfilepages matches. +func TestCustomLogFilePages(t *testing.T) { + t.Parallel() + cli, err := client.NewClientWithOpts(client.FromEnv) + if err != nil { + t.Fatal(err) + } + containerConfig := container.Config{ + Env: []string{"LICENSE=accept", "MQ_QMGR_LOG_FILE_PAGES=8192", "MQ_QMGR_NAME=qmlfp"}, + } + + id := runContainer(t, cli, &containerConfig) + defer cleanContainer(t, cli, id) + waitForReady(t, cli, id) + + testLogFilePages(t, cli, id, "qmlfp", "8192") +} diff --git a/test/docker/docker_api_test_util.go b/test/docker/docker_api_test_util.go index 250c32b..ad9a823 100644 --- a/test/docker/docker_api_test_util.go +++ b/test/docker/docker_api_test_util.go @@ -928,3 +928,13 @@ func startContainerError(t *testing.T, cli *client.Client, ID string) error { return nil } + +//testLogFilePages validates that the specified number of logFilePages is present in the qm.ini file. +func testLogFilePages(t *testing.T, cli *client.Client, id string, qmName string, expectedLogFilePages string) { + catIniFileCommand := fmt.Sprintf("cat /var/mqm/qmgrs/" + qmName + "/qm.ini") + _, iniContent := execContainer(t, cli, id, "", []string{"bash", "-c", catIniFileCommand}) + + if !strings.Contains(iniContent, "LogFilePages="+expectedLogFilePages) { + t.Errorf("Expected qm.ini to contain LogFilePages="+expectedLogFilePages+"; got qm.ini \"%v\"", iniContent) + } +}