Add web server to dev image
This commit is contained in:
12
Makefile
12
Makefile
@@ -24,6 +24,9 @@ MQ_VERSION ?= 9.0.4.0
|
|||||||
# be installed. The default value is derived from MQ_VERSION, BASE_IMAGE and architecture
|
# be installed. The default value is derived from MQ_VERSION, BASE_IMAGE and architecture
|
||||||
# Does not apply to MQ Advanced for Developers.
|
# Does not apply to MQ Advanced for Developers.
|
||||||
MQ_ARCHIVE ?= IBM_MQ_$(MQ_VERSION)_$(MQ_ARCHIVE_TYPE)_$(MQ_ARCHIVE_ARCH).tar.gz
|
MQ_ARCHIVE ?= IBM_MQ_$(MQ_VERSION)_$(MQ_ARCHIVE_TYPE)_$(MQ_ARCHIVE_ARCH).tar.gz
|
||||||
|
# MQ_ARCHIVE_DEV is the name of the file, under the downloads directory, from which MQ Advanced
|
||||||
|
# for Developers can be installed
|
||||||
|
MQ_ARCHIVE_DEV ?= $(MQ_ARCHIVE_DEV_$(MQ_VERSION))
|
||||||
# Options to `go test` for the Docker tests
|
# Options to `go test` for the Docker tests
|
||||||
TEST_OPTS_DOCKER ?=
|
TEST_OPTS_DOCKER ?=
|
||||||
# Options to `go test` for the Kubernetes tests
|
# Options to `go test` for the Kubernetes tests
|
||||||
@@ -68,9 +71,6 @@ endif
|
|||||||
# Archive names for IBM MQ Advanced for Developers for Ubuntu
|
# Archive names for IBM MQ Advanced for Developers for Ubuntu
|
||||||
MQ_ARCHIVE_DEV_9.0.3.0=mqadv_dev903_ubuntu_x86-64.tar.gz
|
MQ_ARCHIVE_DEV_9.0.3.0=mqadv_dev903_ubuntu_x86-64.tar.gz
|
||||||
MQ_ARCHIVE_DEV_9.0.4.0=mqadv_dev904_ubuntu_x86-64.tar.gz
|
MQ_ARCHIVE_DEV_9.0.4.0=mqadv_dev904_ubuntu_x86-64.tar.gz
|
||||||
# MQ_ARCHIVE_DEV is the name of the file, under the downloads directory, from which MQ Advanced
|
|
||||||
# for Developers can be installed
|
|
||||||
MQ_ARCHIVE_DEV=$(MQ_ARCHIVE_DEV_$(MQ_VERSION))
|
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# Build targets
|
# Build targets
|
||||||
@@ -140,7 +140,7 @@ test-advancedserver: test/docker/vendor
|
|||||||
.PHONY: test-devserver
|
.PHONY: test-devserver
|
||||||
test-devserver: test/docker/vendor
|
test-devserver: test/docker/vendor
|
||||||
$(info $(SPACER)$(shell printf $(TITLE)"Test $(DOCKER_FULL_DEVSERVER) on Docker"$(END)))
|
$(info $(SPACER)$(shell printf $(TITLE)"Test $(DOCKER_FULL_DEVSERVER) on Docker"$(END)))
|
||||||
cd test/docker && TEST_IMAGE=$(MQ_IMAGE_DEVSERVER) go test -parallel $(NUM_CPU) $(TEST_OPTS_DOCKER)
|
cd test/docker && TEST_IMAGE=$(MQ_IMAGE_DEVSERVER) go test -parallel $(NUM_CPU) -tags mqdev $(TEST_OPTS_DOCKER)
|
||||||
|
|
||||||
.PHONY: test-advancedserver-cover
|
.PHONY: test-advancedserver-cover
|
||||||
test-advancedserver-cover: test/docker/vendor
|
test-advancedserver-cover: test/docker/vendor
|
||||||
@@ -218,11 +218,13 @@ build-advancedserver: downloads/$(MQ_ARCHIVE) docker-version
|
|||||||
$(call docker-build-mq,$(MQ_IMAGE_ADVANCEDSERVER),Dockerfile-server,$(MQ_ARCHIVE),"4486e8c4cc9146fd9b3ce1f14a2dfc5b","IBM MQ Advanced",$(MQ_VERSION))
|
$(call docker-build-mq,$(MQ_IMAGE_ADVANCEDSERVER),Dockerfile-server,$(MQ_ARCHIVE),"4486e8c4cc9146fd9b3ce1f14a2dfc5b","IBM MQ Advanced",$(MQ_VERSION))
|
||||||
|
|
||||||
.PHONY: build-devserver
|
.PHONY: build-devserver
|
||||||
|
# Target-specific variable to add web server into devserver image
|
||||||
|
build-devserver: MQ_PACKAGES=ibmmq-server ibmmq-java ibmmq-jre ibmmq-gskit ibmmq-msg-.* ibmmq-samples ibmmq-ams ibmmq-web
|
||||||
build-devserver: downloads/$(MQ_ARCHIVE_DEV) docker-version
|
build-devserver: downloads/$(MQ_ARCHIVE_DEV) docker-version
|
||||||
@test "$(shell uname -m)" = "x86_64" || (echo "Error: MQ Advanced for Developers is only available for x86_64 architecture" && exit 1)
|
@test "$(shell uname -m)" = "x86_64" || (echo "Error: MQ Advanced for Developers is only available for x86_64 architecture" && exit 1)
|
||||||
$(info $(shell printf $(TITLE)"Build $(MQ_IMAGE_DEVSERVER_BASE)"$(END)))
|
$(info $(shell printf $(TITLE)"Build $(MQ_IMAGE_DEVSERVER_BASE)"$(END)))
|
||||||
$(call docker-build-mq,$(MQ_IMAGE_DEVSERVER_BASE),Dockerfile-server,$(MQ_ARCHIVE_DEV),"98102d16795c4263ad9ca075190a2d4d","IBM MQ Advanced for Developers (Non-Warranted)",$(MQ_VERSION))
|
$(call docker-build-mq,$(MQ_IMAGE_DEVSERVER_BASE),Dockerfile-server,$(MQ_ARCHIVE_DEV),"98102d16795c4263ad9ca075190a2d4d","IBM MQ Advanced for Developers (Non-Warranted)",$(MQ_VERSION))
|
||||||
docker build --tag $(MQ_IMAGE_DEVSERVER) incubating/mqadvanced-server-dev
|
docker build --tag $(MQ_IMAGE_DEVSERVER) --file incubating/mqadvanced-server-dev/Dockerfile .
|
||||||
|
|
||||||
.PHONY: build-advancedserver-cover
|
.PHONY: build-advancedserver-cover
|
||||||
build-advancedserver-cover: docker-version
|
build-advancedserver-cover: docker-version
|
||||||
|
|||||||
133
cmd/runmqdevserver/main.go
Normal file
133
cmd/runmqdevserver/main.go
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
/*
|
||||||
|
© Copyright IBM Corporation 2018
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/ibm-messaging/mq-container/internal/command"
|
||||||
|
"github.com/ibm-messaging/mq-container/internal/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
var log *logger.Logger
|
||||||
|
|
||||||
|
func setPassword(user string, password string) error {
|
||||||
|
cmd := exec.Command("chpasswd")
|
||||||
|
stdin, err := cmd.StdinPipe()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Fprintf(stdin, "%s:%s", user, password)
|
||||||
|
stdin.Close()
|
||||||
|
_, _, err = command.RunCmd(cmd)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Printf("Set password for \"%v\" user", user)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getLogFormat() string {
|
||||||
|
return os.Getenv("LOG_FORMAT")
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDebug() bool {
|
||||||
|
debug := os.Getenv("DEBUG")
|
||||||
|
if debug == "true" || debug == "1" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func configureLogger() error {
|
||||||
|
f := getLogFormat()
|
||||||
|
d := getDebug()
|
||||||
|
switch f {
|
||||||
|
case "json":
|
||||||
|
log = logger.NewLogger(os.Stderr, d, true)
|
||||||
|
case "basic":
|
||||||
|
log = logger.NewLogger(os.Stderr, d, false)
|
||||||
|
default:
|
||||||
|
log = logger.NewLogger(os.Stdout, d, false)
|
||||||
|
return fmt.Errorf("invalid value for LOG_FORMAT: %v", f)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func logTerminationf(format string, args ...interface{}) {
|
||||||
|
logTermination(fmt.Sprintf(format, args))
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Duplicated code
|
||||||
|
func logTermination(args ...interface{}) {
|
||||||
|
msg := fmt.Sprint(args)
|
||||||
|
// Write the message to the termination log. This is the default place
|
||||||
|
// that Kubernetes will look for termination information.
|
||||||
|
log.Debugf("Writing termination message: %v", msg)
|
||||||
|
err := ioutil.WriteFile("/dev/termination-log", []byte(msg), 0660)
|
||||||
|
if err != nil {
|
||||||
|
log.Debug(err)
|
||||||
|
}
|
||||||
|
log.Error(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func doMain() error {
|
||||||
|
err := configureLogger()
|
||||||
|
if err != nil {
|
||||||
|
logTermination(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
adminPassword, set := os.LookupEnv("MQ_ADMIN_PASSWORD")
|
||||||
|
if set {
|
||||||
|
err = setPassword("admin", adminPassword)
|
||||||
|
if err != nil {
|
||||||
|
logTerminationf("Error setting admin password: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
appPassword, set := os.LookupEnv("MQ_APP_PASSWORD")
|
||||||
|
if set {
|
||||||
|
err = setPassword("app", appPassword)
|
||||||
|
if err != nil {
|
||||||
|
logTerminationf("Error setting app password: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = updateMQSC(set)
|
||||||
|
if err != nil {
|
||||||
|
logTerminationf("Error updating MQSC: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var osExit = os.Exit
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
err := doMain()
|
||||||
|
if err != nil {
|
||||||
|
osExit(1)
|
||||||
|
} else {
|
||||||
|
// Replace this process with runmqserver
|
||||||
|
syscall.Exec("/usr/local/bin/runmqserver", []string{"runmqserver"}, os.Environ())
|
||||||
|
}
|
||||||
|
}
|
||||||
57
cmd/runmqdevserver/mqsc.go
Normal file
57
cmd/runmqdevserver/mqsc.go
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
© Copyright IBM Corporation 2018
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"html/template"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func updateMQSC(appPasswordRequired bool) error {
|
||||||
|
var checkClient string
|
||||||
|
if appPasswordRequired {
|
||||||
|
checkClient = "REQUIRED"
|
||||||
|
} else {
|
||||||
|
checkClient = "ASQMGR"
|
||||||
|
}
|
||||||
|
const mqsc string = "/etc/mqm/dev.mqsc"
|
||||||
|
if os.Getenv("MQ_DEV") == "true" {
|
||||||
|
const mqscTemplate string = mqsc + ".tpl"
|
||||||
|
// Re-configure channel if app password not set
|
||||||
|
t, err := template.ParseFiles(mqscTemplate)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
f, err := os.OpenFile(mqsc, os.O_CREATE|os.O_WRONLY, 0660)
|
||||||
|
defer f.Close()
|
||||||
|
err = t.Execute(f, map[string]string{"ChckClnt": checkClient})
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// TODO: Lookup value for MQM user here?
|
||||||
|
err = os.Chown(mqsc, 999, 999)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// os.Remove(mqscTemplate)
|
||||||
|
} else {
|
||||||
|
os.Remove(mqsc)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -68,6 +68,12 @@ func doMain() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = postInit(name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
newQM, err := createQueueManager(name)
|
newQM, err := createQueueManager(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logTermination(err)
|
logTermination(err)
|
||||||
|
|||||||
@@ -61,7 +61,9 @@ func mirrorAvailableMessages(f *os.File, mf mirrorFunc) {
|
|||||||
mf(t)
|
mf(t)
|
||||||
count++
|
count++
|
||||||
}
|
}
|
||||||
log.Debugf("Mirrored %v log entries from %v", count, f.Name())
|
if count > 0 {
|
||||||
|
log.Debugf("Mirrored %v log entries from %v", count, f.Name())
|
||||||
|
}
|
||||||
err := scanner.Err()
|
err := scanner.Err()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Error reading file %v: %v", f.Name(), err)
|
log.Errorf("Error reading file %v: %v", f.Name(), err)
|
||||||
|
|||||||
36
cmd/runmqserver/post_init_dev.go
Normal file
36
cmd/runmqserver/post_init_dev.go
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
// +build mqdev
|
||||||
|
|
||||||
|
/*
|
||||||
|
© Copyright IBM Corporation 2018
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "os"
|
||||||
|
|
||||||
|
// postInit is run after /var/mqm is set up
|
||||||
|
// This version of postInit is only included as part of the MQ Advanced for Developers build
|
||||||
|
func postInit(name string) error {
|
||||||
|
disable := os.Getenv("MQ_DISABLE_WEB_CONSOLE")
|
||||||
|
if disable != "true" && disable != "1" {
|
||||||
|
// Configure and start the web server, in the background (if installed)
|
||||||
|
// WARNING: No error handling or health checking available for the web server,
|
||||||
|
// which is why it's limited to use with MQ Advanced for Developers only
|
||||||
|
go func() {
|
||||||
|
configureWebServer()
|
||||||
|
startWebServer()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
22
cmd/runmqserver/post_init_other.go
Normal file
22
cmd/runmqserver/post_init_other.go
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
// +build !mqdev
|
||||||
|
|
||||||
|
/*
|
||||||
|
© Copyright IBM Corporation 2018
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
package main
|
||||||
|
|
||||||
|
func postInit(name string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
110
cmd/runmqserver/webserver.go
Normal file
110
cmd/runmqserver/webserver.go
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
// +build mqdev
|
||||||
|
|
||||||
|
/*
|
||||||
|
© Copyright IBM Corporation 2018
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/ibm-messaging/mq-container/internal/command"
|
||||||
|
)
|
||||||
|
|
||||||
|
func startWebServer() error {
|
||||||
|
_, err := os.Stat("/opt/mqm/bin/strmqweb")
|
||||||
|
if err != nil && os.IsNotExist(err) {
|
||||||
|
log.Debug("Skipping web server, because it's not installed")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
log.Println("Starting web server")
|
||||||
|
out, rc, err := command.RunAsMQM("strmqweb")
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error %v starting web server: %v", rc, string(out))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Println("Started web server")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func configureWebServer() error {
|
||||||
|
_, err := os.Stat("/opt/mqm/bin/strmqweb")
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
const webConfigDir string = "/etc/mqm/web"
|
||||||
|
_, err = os.Stat(webConfigDir)
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
uid, gid, err := lookupMQM()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
const prefix string = "/etc/mqm/web"
|
||||||
|
err = filepath.Walk(prefix, func(from string, info os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
to := fmt.Sprintf("/var/mqm/web%v", from[len(prefix):])
|
||||||
|
exists := true
|
||||||
|
_, err = os.Stat(to)
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
exists = false
|
||||||
|
} else {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if info.IsDir() {
|
||||||
|
if !exists {
|
||||||
|
err := os.MkdirAll(to, 0770)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.Printf("Directory: %v --> %v", from, to)
|
||||||
|
} else {
|
||||||
|
if exists {
|
||||||
|
err := os.Remove(to)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO: Permissions. Can't rely on them being set in Dockerfile
|
||||||
|
err := os.Link(from, to)
|
||||||
|
if err != nil {
|
||||||
|
log.Debug(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Printf("File: %v", from)
|
||||||
|
}
|
||||||
|
err = os.Chown(to, uid, gid)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
}
|
||||||
@@ -29,6 +29,8 @@ MQ_ARCHIVE=mq-1.2.3.4.tar.gz MQ_VERSION=1.2.3.4 make build-advancedserver
|
|||||||
## Building a developer image
|
## Building a developer image
|
||||||
Run `make build-devserver`, which will download the latest version of MQ Advanced for Developers from IBM developerWorks. This is currently only available on the `x86_64` architecture.
|
Run `make build-devserver`, which will download the latest version of MQ Advanced for Developers from IBM developerWorks. This is currently only available on the `x86_64` architecture.
|
||||||
|
|
||||||
|
You can use the environment variable `MQ_ARCHIVE_DEV` to specify an alternative local file to install from (which must be in the `downloads` directory).
|
||||||
|
|
||||||
## Building on a different base image
|
## Building on a different base image
|
||||||
By default, the MQ images use Ubuntu as the base layer. You can build using a Red Hat Enterprise Linux compatible base layer by setting the `BASE_IMAGE` environment variable. For example:
|
By default, the MQ images use Ubuntu as the base layer. You can build using a Red Hat Enterprise Linux compatible base layer by setting the `BASE_IMAGE` environment variable. For example:
|
||||||
|
|
||||||
|
|||||||
@@ -12,19 +12,45 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Build stage to build Go code
|
||||||
|
###############################################################################
|
||||||
|
FROM golang:1.9 as builder
|
||||||
|
WORKDIR /go/src/github.com/ibm-messaging/mq-container/
|
||||||
|
COPY cmd/ ./cmd
|
||||||
|
COPY internal/ ./internal
|
||||||
|
COPY vendor/ ./vendor
|
||||||
|
# Re-build runmqserver, with code tagged with 'mqdev' enabled
|
||||||
|
RUN go build --tags 'mqdev' ./cmd/runmqserver
|
||||||
|
RUN go build ./cmd/runmqdevserver/
|
||||||
|
# Run all unit tests
|
||||||
|
RUN go test -v ./cmd/runmqdevserver/...
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Main build stage
|
||||||
|
###############################################################################
|
||||||
FROM mqadvanced-server-dev-base:9.0.4.0-x86_64-ubuntu-16.04
|
FROM mqadvanced-server-dev-base:9.0.4.0-x86_64-ubuntu-16.04
|
||||||
|
|
||||||
|
# Enable MQ developer default configuration
|
||||||
|
ENV MQ_DEV=true
|
||||||
|
|
||||||
|
# Default administrator password
|
||||||
|
ENV MQ_ADMIN_PASSWORD=passw0rd
|
||||||
|
|
||||||
## Add admin and app users, and set a default password for admin
|
## Add admin and app users, and set a default password for admin
|
||||||
RUN useradd admin -G mqm \
|
RUN useradd admin -G mqm \
|
||||||
&& groupadd mqclient \
|
&& groupadd mqclient \
|
||||||
&& useradd app -G mqclient,mqm \
|
&& useradd app -G mqclient,mqm \
|
||||||
&& echo admin:passw0rd | chpasswd
|
&& echo admin:$MQ_ADMIN_PASSWORD | chpasswd
|
||||||
|
|
||||||
COPY dev.mqsc /etc/mqm/
|
COPY --from=builder /go/src/github.com/ibm-messaging/mq-container/runmqserver /usr/local/bin/
|
||||||
COPY entrypoint.sh /usr/local/bin/
|
COPY --from=builder /go/src/github.com/ibm-messaging/mq-container/runmqdevserver /usr/local/bin/
|
||||||
RUN chmod +x /usr/local/bin/entrypoint.sh
|
# Copy template MQSC for default developer configuration
|
||||||
|
COPY incubating/mqadvanced-server-dev/dev.mqsc.tpl /etc/mqm/
|
||||||
|
# Copy web XML files for default developer configuration
|
||||||
|
COPY incubating/mqadvanced-server-dev/web /etc/mqm/web
|
||||||
|
RUN chmod +x /usr/local/bin/runmq*
|
||||||
|
|
||||||
# Enable MQ developer default configuration
|
EXPOSE 9443
|
||||||
ENV MQ_DEV=true
|
|
||||||
|
|
||||||
ENTRYPOINT ["entrypoint.sh"]
|
ENTRYPOINT ["runmqdevserver"]
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
* © Copyright IBM Corporation 2017
|
* © Copyright IBM Corporation 2017, 2018
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@@ -38,7 +38,7 @@ DEFINE CHANNEL('DEV.APP.SVRCONN') CHLTYPE(SVRCONN) REPLACE
|
|||||||
|
|
||||||
* Developer channel authentication rules
|
* Developer channel authentication rules
|
||||||
SET CHLAUTH('*') TYPE(ADDRESSMAP) ADDRESS('*') USERSRC(NOACCESS) DESCR('Back-stop rule - Blocks everyone') ACTION(REPLACE)
|
SET CHLAUTH('*') TYPE(ADDRESSMAP) ADDRESS('*') USERSRC(NOACCESS) DESCR('Back-stop rule - Blocks everyone') ACTION(REPLACE)
|
||||||
SET CHLAUTH('DEV.APP.SVRCONN') TYPE(ADDRESSMAP) ADDRESS('*') USERSRC(CHANNEL) CHCKCLNT(REQUIRED) DESCR('Allows connection via APP channel') ACTION(REPLACE)
|
SET CHLAUTH('DEV.APP.SVRCONN') TYPE(ADDRESSMAP) ADDRESS('*') USERSRC(CHANNEL) CHCKCLNT({{ .ChckClnt }}) DESCR('Allows connection via APP channel') ACTION(REPLACE)
|
||||||
SET CHLAUTH('DEV.ADMIN.SVRCONN') TYPE(BLOCKUSER) USERLIST('nobody') DESCR('Allows admins on ADMIN channel') ACTION(REPLACE)
|
SET CHLAUTH('DEV.ADMIN.SVRCONN') TYPE(BLOCKUSER) USERLIST('nobody') DESCR('Allows admins on ADMIN channel') ACTION(REPLACE)
|
||||||
SET CHLAUTH('DEV.ADMIN.SVRCONN') TYPE(USERMAP) CLNTUSER('admin') USERSRC(CHANNEL) DESCR('Allows admin user to connect via ADMIN channel') ACTION(REPLACE)
|
SET CHLAUTH('DEV.ADMIN.SVRCONN') TYPE(USERMAP) CLNTUSER('admin') USERSRC(CHANNEL) DESCR('Allows admin user to connect via ADMIN channel') ACTION(REPLACE)
|
||||||
|
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<server>
|
||||||
|
<featureManager>
|
||||||
|
<feature>appSecurity-2.0</feature>
|
||||||
|
<feature>basicAuthenticationMQ-1.0</feature>
|
||||||
|
</featureManager>
|
||||||
|
<enterpriseApplication id="com.ibm.mq.console">
|
||||||
|
<application-bnd>
|
||||||
|
<security-role name="MQWebAdmin">
|
||||||
|
<group name="MQWebUI" realm="defaultRealm"/>
|
||||||
|
</security-role>
|
||||||
|
</application-bnd>
|
||||||
|
</enterpriseApplication>
|
||||||
|
<enterpriseApplication id="com.ibm.mq.rest">
|
||||||
|
<application-bnd>
|
||||||
|
<security-role name="MQWebAdmin">
|
||||||
|
<group name="MQWebUI" realm="defaultRealm"/>
|
||||||
|
</security-role>
|
||||||
|
</application-bnd>
|
||||||
|
</enterpriseApplication>
|
||||||
|
<basicRegistry id="basic" realm="defaultRealm">
|
||||||
|
<user name="admin" password="${env.MQ_ADMIN_PASSWORD}"/>
|
||||||
|
<group name="MQWebUI">
|
||||||
|
<member name="admin"/>
|
||||||
|
</group>
|
||||||
|
</basicRegistry>
|
||||||
|
<variable name="httpHost" value="*"/>
|
||||||
|
<httpDispatcher enableWelcomePage="false" appOrContextRootMissingMessage='Redirecting to console.<script>document.location.href="/ibmmq/console";</script>' />
|
||||||
|
<include location="tls.xml"/>
|
||||||
|
</server>
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
<keyStore id="MQWebKeyStore" location="/var/mqm/web/installations/Installation1/servers/mqweb/key.jks" type="JKS" password="${env.MQ_TLS_PASSPHRASE}"/>
|
||||||
|
<keyStore id="MQWebTrustStore" location="/var/mqm/web/installations/Installation1/servers/mqweb/trust.jks" type="JKS" password="${env.MQ_TLS_PASSPHRASE}"/>
|
||||||
|
<ssl id="thisSSLConfig" clientAuthenticationSupported="true" keyStoreRef="MQWebKeyStore" trustStoreRef="MQWebTrustStore" sslProtocol="TLSv1.2" serverKeyAlias="webcert"/>
|
||||||
|
<sslDefault sslRef="thisSSLConfig"/>
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
<sslDefault sslRef="mqDefaultSSLConfig"/>
|
||||||
@@ -22,7 +22,7 @@ test -f /usr/bin/yum && RHEL=true || RHEL=false
|
|||||||
test -f /usr/bin/apt-get && UBUNTU=true || UBUNTU=false
|
test -f /usr/bin/apt-get && UBUNTU=true || UBUNTU=false
|
||||||
|
|
||||||
# If MQ_PACKAGES isn't specifically set, then choose a valid set of defaults
|
# If MQ_PACKAGES isn't specifically set, then choose a valid set of defaults
|
||||||
if [ -z $MQ_PACKAGES ]; then
|
if [ -z "$MQ_PACKAGES" ]; then
|
||||||
$UBUNTU && MQ_PACKAGES="ibmmq-server ibmmq-java ibmmq-jre ibmmq-gskit ibmmq-msg-.* ibmmq-samples ibmmq-ams"
|
$UBUNTU && MQ_PACKAGES="ibmmq-server ibmmq-java ibmmq-jre ibmmq-gskit ibmmq-msg-.* ibmmq-samples ibmmq-ams"
|
||||||
$RHEL && MQ_PACKAGES="MQSeriesRuntime-*.rpm MQSeriesServer-*.rpm MQSeriesJava*.rpm MQSeriesJRE*.rpm MQSeriesGSKit*.rpm MQSeriesMsg*.rpm MQSeriesSamples*.rpm MQSeriesAMS-*.rpm"
|
$RHEL && MQ_PACKAGES="MQSeriesRuntime-*.rpm MQSeriesServer-*.rpm MQSeriesJava*.rpm MQSeriesJRE*.rpm MQSeriesGSKit*.rpm MQSeriesMsg*.rpm MQSeriesSamples*.rpm MQSeriesAMS-*.rpm"
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -20,16 +20,17 @@ package command
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"os/user"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strconv"
|
||||||
"syscall"
|
"syscall"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Run runs an OS command. On Linux it waits for the command to
|
// RunCmd runs an OS command. On Linux it waits for the command to
|
||||||
// complete and returns the exit status (return code).
|
// complete and returns the exit status (return code).
|
||||||
// Do not use this function to run shell built-ins (like "cd"), because
|
// Do not use this function to run shell built-ins (like "cd"), because
|
||||||
// the error handling works differently
|
// the error handling works differently
|
||||||
func Run(name string, arg ...string) (string, int, error) {
|
func RunCmd(cmd *exec.Cmd) (string, int, error) {
|
||||||
cmd := exec.Command(name, arg...)
|
|
||||||
// Run the command and wait for completion
|
// Run the command and wait for completion
|
||||||
out, err := cmd.CombinedOutput()
|
out, err := cmd.CombinedOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -39,10 +40,47 @@ func Run(name string, arg ...string) (string, int, error) {
|
|||||||
if ok && runtime.GOOS == "linux" {
|
if ok && runtime.GOOS == "linux" {
|
||||||
status, ok := exiterr.Sys().(syscall.WaitStatus)
|
status, ok := exiterr.Sys().(syscall.WaitStatus)
|
||||||
if ok {
|
if ok {
|
||||||
return string(out), status.ExitStatus(), fmt.Errorf("%v: %v", name, err)
|
return string(out), status.ExitStatus(), fmt.Errorf("%v: %v", cmd.Path, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return string(out), -1, err
|
return string(out), -1, err
|
||||||
}
|
}
|
||||||
return string(out), 0, nil
|
return string(out), 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Run runs an OS command. On Linux it waits for the command to
|
||||||
|
// complete and returns the exit status (return code).
|
||||||
|
// Do not use this function to run shell built-ins (like "cd"), because
|
||||||
|
// the error handling works differently
|
||||||
|
func Run(name string, arg ...string) (string, int, error) {
|
||||||
|
return RunCmd(exec.Command(name, arg...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunAsMQM runs the specified command as the mqm user
|
||||||
|
func RunAsMQM(name string, arg ...string) (string, int, error) {
|
||||||
|
cmd := exec.Command(name, arg...)
|
||||||
|
cmd.SysProcAttr = &syscall.SysProcAttr{}
|
||||||
|
uid, gid, err := lookupMQM()
|
||||||
|
if err != nil {
|
||||||
|
return "", 0, err
|
||||||
|
}
|
||||||
|
cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)}
|
||||||
|
return RunCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Duplicated code
|
||||||
|
func lookupMQM() (int, int, error) {
|
||||||
|
mqm, err := user.Lookup("mqm")
|
||||||
|
if err != nil {
|
||||||
|
return -1, -1, err
|
||||||
|
}
|
||||||
|
mqmUID, err := strconv.Atoi(mqm.Uid)
|
||||||
|
if err != nil {
|
||||||
|
return -1, -1, err
|
||||||
|
}
|
||||||
|
mqmGID, err := strconv.Atoi(mqm.Gid)
|
||||||
|
if err != nil {
|
||||||
|
return -1, -1, err
|
||||||
|
}
|
||||||
|
return mqmUID, mqmGID, nil
|
||||||
|
}
|
||||||
|
|||||||
72
test/docker/devconfig_test.go
Normal file
72
test/docker/devconfig_test.go
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
// +build mqdev
|
||||||
|
|
||||||
|
/*
|
||||||
|
© Copyright IBM Corporation 2018
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api/types/container"
|
||||||
|
"github.com/docker/docker/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDevGoldenPath(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
cli, err := client.NewEnvClient()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
containerConfig := container.Config{
|
||||||
|
Env: []string{"LICENSE=accept", "MQ_QMGR_NAME=qm1"},
|
||||||
|
}
|
||||||
|
id := runContainer(t, cli, &containerConfig)
|
||||||
|
|
||||||
|
defer cleanContainer(t, cli, id)
|
||||||
|
waitForReady(t, cli, id)
|
||||||
|
waitForWebReady(t, cli, id)
|
||||||
|
|
||||||
|
timeout := time.Duration(30 * time.Second)
|
||||||
|
// Disable TLS verification (server uses a self-signed certificate by default,
|
||||||
|
// so verification isn't useful anyway)
|
||||||
|
tr := &http.Transport{
|
||||||
|
TLSClientConfig: &tls.Config{
|
||||||
|
InsecureSkipVerify: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
httpClient := http.Client{
|
||||||
|
Timeout: timeout,
|
||||||
|
Transport: tr,
|
||||||
|
}
|
||||||
|
|
||||||
|
url := fmt.Sprintf("https://localhost:%s/ibmmq/rest/v1/admin/installation", getWebPort(t, cli, id))
|
||||||
|
req, err := http.NewRequest("GET", url, nil)
|
||||||
|
req.SetBasicAuth("admin", "passw0rd")
|
||||||
|
resp, err := httpClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
t.Errorf("Expected HTTP status code %v from 'GET installation'; got %v", http.StatusOK, resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop the container cleanly
|
||||||
|
stopContainer(t, cli, id)
|
||||||
|
}
|
||||||
42
test/docker/devconfig_test_util.go
Normal file
42
test/docker/devconfig_test_util.go
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
// +build mqdev
|
||||||
|
|
||||||
|
/*
|
||||||
|
© Copyright IBM Corporation 2018
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/docker/docker/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
func waitForWebReady(t *testing.T, cli *client.Client, ID string) {
|
||||||
|
config := tls.Config{InsecureSkipVerify: true}
|
||||||
|
a := fmt.Sprintf("localhost:%s", getWebPort(t, cli, ID))
|
||||||
|
for {
|
||||||
|
conn, err := tls.Dial("tcp", a, &config)
|
||||||
|
if err == nil {
|
||||||
|
conn.Close()
|
||||||
|
// Extra sleep to allow web apps to start
|
||||||
|
time.Sleep(3 * time.Second)
|
||||||
|
t.Log("MQ web server is ready")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -39,6 +39,7 @@ import (
|
|||||||
"github.com/docker/docker/client"
|
"github.com/docker/docker/client"
|
||||||
"github.com/docker/docker/pkg/jsonmessage"
|
"github.com/docker/docker/pkg/jsonmessage"
|
||||||
"github.com/docker/docker/pkg/stdcopy"
|
"github.com/docker/docker/pkg/stdcopy"
|
||||||
|
"github.com/docker/go-connections/nat"
|
||||||
)
|
)
|
||||||
|
|
||||||
func imageName() string {
|
func imageName() string {
|
||||||
@@ -166,6 +167,15 @@ func runContainer(t *testing.T, cli *client.Client, containerConfig *container.C
|
|||||||
coverageBind(t),
|
coverageBind(t),
|
||||||
terminationBind(t),
|
terminationBind(t),
|
||||||
},
|
},
|
||||||
|
// Assign a random port for the web server on the host
|
||||||
|
// TODO: Don't do this for all tests
|
||||||
|
PortBindings: nat.PortMap{
|
||||||
|
"9443/tcp": []nat.PortBinding{
|
||||||
|
{
|
||||||
|
HostIP: "0.0.0.0",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
networkingConfig := network.NetworkingConfig{}
|
networkingConfig := network.NetworkingConfig{}
|
||||||
t.Logf("Running container (%s)", containerConfig.Image)
|
t.Logf("Running container (%s)", containerConfig.Image)
|
||||||
@@ -493,3 +503,11 @@ func copyFromContainer(t *testing.T, cli *client.Client, id string, file string)
|
|||||||
}
|
}
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getWebPort(t *testing.T, cli *client.Client, ID string) string {
|
||||||
|
i, err := cli.ContainerInspect(context.Background(), ID)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return i.NetworkSettings.Ports["9443/tcp"][0].HostPort
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user