diff --git a/Makefile-RHEL b/Makefile-RHEL index ab1b2bf..6185c33 100644 --- a/Makefile-RHEL +++ b/Makefile-RHEL @@ -181,7 +181,7 @@ build-mqgolang-sdk: check-prereqs downloads/$(MQ_SDK_ARCHIVE) .PHONY: build-go-programs build-go-programs: check-prereqs downloads/$(MQ_SDK_ARCHIVE) build-mqgolang-sdk $(info $(SPACER)$(shell printf $(TITLE)"Build go programs"$(END))) - IMAGE_REVISION=$(IMAGE_REVISION) IMAGE_SOURCE=$(IMAGE_SOURCE) sudo mq-advanced-server-rhel/go-buildah.sh "$(MQ_IMAGE_GOLANG_SDK)" "$(MQDEV)" + IMAGE_REVISION=$(IMAGE_REVISION) IMAGE_SOURCE=$(IMAGE_SOURCE) sudo --preserve-env mq-advanced-server-rhel/go-buildah.sh "$(MQ_IMAGE_GOLANG_SDK)" "$(MQDEV)" .PHONY: build-devjmstest build-devjmstest: check-test-prereqs diff --git a/cmd/runmqdevserver/logruntime.go b/cmd/runmqdevserver/logruntime.go index 21aae84..efa9694 100644 --- a/cmd/runmqdevserver/logruntime.go +++ b/cmd/runmqdevserver/logruntime.go @@ -46,12 +46,17 @@ func logContainerDetails() { } } caps, err := containerruntime.GetCapabilities() + capLogged := false if err == nil { for k, v := range caps { if len(v) > 0 { log.Printf("Capabilities (%s set): %v", strings.ToLower(k), strings.Join(v, ",")) + capLogged = true } } + if !capLogged { + log.Print("Capabilities: none") + } } else { log.Errorf("Error getting capabilities: %v", err) } diff --git a/cmd/runmqserver/logruntime.go b/cmd/runmqserver/logruntime.go index 10f1539..2f552f0 100644 --- a/cmd/runmqserver/logruntime.go +++ b/cmd/runmqserver/logruntime.go @@ -50,12 +50,19 @@ func logContainerDetails() error { } } caps, err := containerruntime.GetCapabilities() + capLogged := false if err == nil { for k, v := range caps { if len(v) > 0 { log.Printf("Capabilities (%s set): %v", strings.ToLower(k), strings.Join(v, ",")) + capLogged = true } } + if !capLogged { + log.Print("Capabilities: none") + } + } else { + log.Errorf("Error getting capabilities: %v", err) } sc, err := containerruntime.GetSeccomp() if err == nil { diff --git a/test/docker/docker_api_test_util.go b/test/docker/docker_api_test_util.go index 2aa1500..f755f5b 100644 --- a/test/docker/docker_api_test_util.go +++ b/test/docker/docker_api_test_util.go @@ -43,6 +43,18 @@ import ( "github.com/docker/go-connections/nat" ) +type containerDetails struct { + ID string + Name string + Image string + Path string + Args []string + CapAdd []string + CapDrop []string + User string + Env []string +} + func imageName() string { image, ok := os.LookupEnv("TEST_IMAGE") if !ok { @@ -59,8 +71,9 @@ func imageNameDevJMS() string { return image } +// baseImage returns the ID of the underlying base image (e.g. "ubuntu" or "rhel") func baseImage(t *testing.T, cli *client.Client) string { - rc, out := runContainerOneShot(t, cli, "bash", "-c", "cat /etc/*release | grep \"^ID=\"") + rc, out := runContainerOneShot(t, cli, "grep", "^ID=", "/etc/os-release") if rc != 0 { t.Fatal("Couldn't determine base image") } @@ -71,6 +84,16 @@ func baseImage(t *testing.T, cli *client.Client) string { return s[1] } +// devImage returns true if the image under test is a developer image, +// determined by use of the MQ_ADMIN_PASSWORD environment variable +func devImage(t *testing.T, cli *client.Client) bool { + rc, _ := runContainerOneShot(t, cli, "printenv", "MQ_ADMIN_PASSWORD") + if rc == 0 { + return true + } + return false +} + // isWSL return whether we are running in the Windows Subsystem for Linux func isWSL(t *testing.T) bool { if runtime.GOOS == "linux" { @@ -166,21 +189,49 @@ func expectTerminationMessage(t *testing.T, cli *client.Client, ID string) { } } -func cleanContainer(t *testing.T, cli *client.Client, ID string) { +// logContainerDetails logs selected details about the container +func logContainerDetails(t *testing.T, cli *client.Client, ID string) { i, err := cli.ContainerInspect(context.Background(), ID) if err == nil { - // Log the results and continue - t.Logf("Inspected container %v: %#v", ID, i) - s, err := json.MarshalIndent(i, "", " ") - if err != nil { - t.Fatal(err) + d := containerDetails{ + ID: ID, + Name: i.Name, + Image: i.Image, + Path: i.Path, + Args: i.Args, + CapAdd: i.HostConfig.CapAdd, + CapDrop: i.HostConfig.CapDrop, + User: i.Config.User, + Env: i.Config.Env, } - t.Logf("Inspected container %v: %v", ID, string(s)) + // If you need more details, you can always just run `json.MarshalIndent(i, "", " ")` to see everything. + t.Logf("Container details: %+v", d) } +} + +func cleanContainerQuiet(t *testing.T, cli *client.Client, ID string) { + timeout := 10 * time.Second + err := cli.ContainerStop(context.Background(), ID, &timeout) + if err != nil { + // Just log the error and continue + t.Log(err) + } + opts := types.ContainerRemoveOptions{ + RemoveVolumes: true, + Force: true, + } + err = cli.ContainerRemove(context.Background(), ID, opts) + if err != nil { + t.Error(err) + } +} + +func cleanContainer(t *testing.T, cli *client.Client, ID string) { + logContainerDetails(t, cli, ID) t.Logf("Stopping container: %v", ID) timeout := 10 * time.Second // Stop the container. This allows the coverage output to be generated. - err = cli.ContainerStop(context.Background(), ID, &timeout) + err := cli.ContainerStop(context.Background(), ID, &timeout) if err != nil { // Just log the error and continue t.Log(err) @@ -208,22 +259,6 @@ func cleanContainer(t *testing.T, cli *client.Client, ID string) { } } -// devImage returns true if the specified image is a developer image, -// determined by use of the MQ_ADMIN_PASSWORD or MQ_APP_PASSWORD -// environment variables -func devImage(t *testing.T, cli *client.Client, imageID string) bool { - i, _, err := cli.ImageInspectWithRaw(context.Background(), imageID) - if err != nil { - t.Fatal(err) - } - for _, e := range i.ContainerConfig.Env { - if strings.HasPrefix(e, "MQ_ADMIN_PASSWORD") || strings.HasPrefix(e, "MQ_APP_PASSWORD") { - return true - } - } - return false -} - // 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. @@ -247,7 +282,7 @@ func runContainerWithPorts(t *testing.T, cli *client.Client, containerConfig *co "ALL", }, } - if devImage(t, cli, containerConfig.Image) { + if devImage(t, cli) { t.Logf("Detected MQ Advanced for Developers image — adding extra Linux capabilities to container") hostConfig.CapAdd = []string{ "CHOWN", @@ -259,6 +294,8 @@ func runContainerWithPorts(t *testing.T, cli *client.Client, containerConfig *co if baseImage(t, cli) != "ubuntu" { hostConfig.CapAdd = append(hostConfig.CapAdd, "DAC_OVERRIDE") } + } else { + t.Logf("Detected MQ Advanced image - dropping all capabilities") } for _, p := range ports { port := nat.Port(fmt.Sprintf("%v/tcp", p)) @@ -295,14 +332,21 @@ func runContainerOneShot(t *testing.T, cli *client.Client, command ...string) (i } hostConfig := container.HostConfig{} networkingConfig := network.NetworkingConfig{} - t.Logf("Running one shot container (%s)", containerConfig.Image) - ctr, err := cli.ContainerCreate(context.Background(), &containerConfig, &hostConfig, &networkingConfig, t.Name()) + t.Logf("Running one shot container (%s): %v", containerConfig.Image, command) + ctr, err := cli.ContainerCreate(context.Background(), &containerConfig, &hostConfig, &networkingConfig, t.Name()+"OneShot") if err != nil { t.Fatal(err) } - startContainer(t, cli, ctr.ID) - defer cleanContainer(t, cli, ctr.ID) - return waitForContainer(t, cli, ctr.ID, 10*time.Second), inspectLogs(t, cli, ctr.ID) + startOptions := types.ContainerStartOptions{} + err = cli.ContainerStart(context.Background(), ctr.ID, startOptions) + if err != nil { + t.Fatal(err) + } + defer cleanContainerQuiet(t, cli, ctr.ID) + rc := waitForContainer(t, cli, ctr.ID, 10*time.Second) + out := inspectLogs(t, cli, ctr.ID) + t.Logf("One shot container finished with rc=%v, output=%v", rc, out) + return rc, out } // runContainerOneShot runs a container with a custom entrypoint, as the root @@ -320,14 +364,20 @@ func runContainerOneShotWithVolume(t *testing.T, cli *client.Client, bind string } networkingConfig := network.NetworkingConfig{} t.Logf("Running one shot container with volume (%s): %v", containerConfig.Image, command) - ctr, err := cli.ContainerCreate(context.Background(), &containerConfig, &hostConfig, &networkingConfig, t.Name()) + ctr, err := cli.ContainerCreate(context.Background(), &containerConfig, &hostConfig, &networkingConfig, t.Name()+"OneShotVolume") if err != nil { t.Fatal(err) } - t.Logf("One shot container ID: %v", ctr.ID) - startContainer(t, cli, ctr.ID) - defer cleanContainer(t, cli, ctr.ID) - return waitForContainer(t, cli, ctr.ID, 10*time.Second), inspectLogs(t, cli, ctr.ID) + startOptions := types.ContainerStartOptions{} + err = cli.ContainerStart(context.Background(), ctr.ID, startOptions) + if err != nil { + t.Fatal(err) + } + defer cleanContainerQuiet(t, cli, ctr.ID) + rc := waitForContainer(t, cli, ctr.ID, 10*time.Second) + out := inspectLogs(t, cli, ctr.ID) + t.Logf("One shot container finished with rc=%v, output=%v", rc, out) + return rc, out } func startContainer(t *testing.T, cli *client.Client, ID string) { diff --git a/test/messaging/buildah.sh b/test/messaging/buildah.sh index 292b4ec..1d8c6a4 100755 --- a/test/messaging/buildah.sh +++ b/test/messaging/buildah.sh @@ -1,5 +1,5 @@ #!/bin/bash -# © Copyright IBM Corporation 2018 +# © Copyright IBM Corporation 2018, 2019 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -22,24 +22,34 @@ set -e # Use a "scratch" container, so the resulting image has minimal files # Resulting image won't have yum, for example -readonly ctr_mq=$(buildah from rhel7) +readonly ctr_mq=$(buildah from rhel7-minimal) readonly mnt_mq=$(buildah mount $ctr_mq) readonly imagename=$1 -buildah run $ctr_mq -- yum install -y \ - java-1.7.0-openjdk-devel \ - java \ - which \ - wget +microdnf_opts="--nodocs" +# Check whether the host is registered with Red Hat +if subscription-manager status ; then + # Host is subscribed, but the minimal image has no enabled repos + # Note that the "bc" package is the only one in "extras" + microdnf_opts="${microdnf_opts} --enablerepo=rhel-7-server-rpms --enablerepo=rhel-7-server-extras-rpms" +else + # Use the Yum repositories configured on the host + cp -R /etc/yum.repos.d/* ${mnt_mq}/etc/yum.repos.d/ +fi +buildah run ${ctr_mq} -- microdnf ${microdnf_opts} install \ + java-1.8.0-openjdk-devel \ + java \ + which \ + wget -buildah run $ctr_mq -- sh -c "cd /tmp && wget http://mirror.olnevhost.net/pub/apache/maven/binaries/apache-maven-3.2.2-bin.tar.gz" -tar xvf $mnt_mq/tmp/apache-maven-3.2.2-bin.tar.gz -C $mnt_mq/tmp/ +buildah run $ctr_mq -- sh -c "cd /tmp && wget https://www-eu.apache.org/dist/maven/maven-3/3.6.0/binaries/apache-maven-3.6.0-bin.tar.gz" +tar xvf $mnt_mq/tmp/apache-maven-3.6.0-bin.tar.gz -C $mnt_mq/tmp/ mkdir -p $mnt_mq/usr/src/mymaven cp pom.xml $mnt_mq/usr/src/mymaven/ cp -R src $mnt_mq/usr/src/mymaven/src -buildah run $ctr_mq -- sh -c "cd /usr/src/mymaven && export M2_HOME=/tmp/apache-maven-3.2.2 && export M2=\$M2_HOME/bin && export PATH=\$M2:\$PATH && mvn --version && mvn dependency:go-offline install && mvn --offline install" +buildah run $ctr_mq -- sh -c "cd /usr/src/mymaven && export M2_HOME=/tmp/apache-maven-3.6.0 && export M2=\$M2_HOME/bin && export PATH=\$M2:\$PATH && mvn --version && mvn dependency:go-offline install && mvn --offline install" mkdir -p $mnt_mq/opt/app @@ -53,13 +63,9 @@ cp $mnt_mq/usr/src/mymaven/target/lib/*.jar $mnt_mq/opt/app/ rm -rf $mnt_mq/tmp/* rm -rf $mnt_mq/usr/src/mymaven -# We can't uninstall tar or gzip because they are required -buildah run $ctr_mq -- yum remove -y \ - wget - # Clean up cached files -buildah run $ctr_mq -- yum clean all -rm -rf ${mnt_mq}/var/cache/yum/* +buildah run ${ctr_mq} -- microdnf ${microdnf_opts} clean all +rm -rf ${mnt_mq}/etc/yum.repos.d/* ############################################################################### # Contain image finalization @@ -69,6 +75,7 @@ buildah config \ --os linux \ --label architecture=x86_64 \ --label name="${imagename%:*}" \ + --cmd "" \ --entrypoint '["java", "-classpath", "/opt/app/*", "org.junit.platform.console.ConsoleLauncher", "-p", "com.ibm.mqcontainer.test", "--details", "verbose"]' \ $ctr_mq buildah unmount $ctr_mq