Change for running as a non-root user (#276)
* Enable running container as mqm * Fix merge problem * Don't force root usage * RHEL image runs as mqm instead of root * Build on host with SELinux enabled * Enable building on node in an OpenShift cluster * Enable running container as mqm * Fix merge problem * Don't force root usage * Merge lastest changes from master * RHEL image runs as mqm instead of root * Fix merge issues * Test changes for non-root * Make timeout properly, and more non-root test fixes * Run tests with fewer/no capabilities * Correct usage docs for non-root * Add security docs * Add temporary debug output * Remove debug code * Fixes for termination-log * Allow init container to run as root * Fixes for CentOS build * Fixes for RHEL build * Logging improvements * Fix Dockerfile RHEL/CentOS build * Fix bash error * Make all builds specify UID * Use redist client for Go SDK * Inspect image before running tests * New test for init container * Log container runtime in runmqdevserver * Add extra capabilities if using a RHEL image
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
// +build mqdev
|
||||
|
||||
/*
|
||||
© 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.
|
||||
@@ -114,7 +114,7 @@ func runJMSTests(t *testing.T, cli *client.Client, ID string, tls bool, user, pa
|
||||
t.Fatal(err)
|
||||
}
|
||||
startContainer(t, cli, ctr.ID)
|
||||
rc := waitForContainer(t, cli, ctr.ID, 10)
|
||||
rc := waitForContainer(t, cli, ctr.ID, 2*time.Minute)
|
||||
if rc != 0 {
|
||||
t.Errorf("JUnit container failed with rc=%v", rc)
|
||||
}
|
||||
|
||||
@@ -47,11 +47,11 @@ func TestLicenseNotSet(t *testing.T) {
|
||||
containerConfig := container.Config{}
|
||||
id := runContainer(t, cli, &containerConfig)
|
||||
defer cleanContainer(t, cli, id)
|
||||
rc := waitForContainer(t, cli, id, 5)
|
||||
rc := waitForContainer(t, cli, id, 10*time.Second)
|
||||
if rc != 1 {
|
||||
t.Errorf("Expected rc=1, got rc=%v", rc)
|
||||
}
|
||||
expectTerminationMessage(t)
|
||||
expectTerminationMessage(t, cli, id)
|
||||
}
|
||||
|
||||
func TestLicenseView(t *testing.T) {
|
||||
@@ -65,7 +65,7 @@ func TestLicenseView(t *testing.T) {
|
||||
}
|
||||
id := runContainer(t, cli, &containerConfig)
|
||||
defer cleanContainer(t, cli, id)
|
||||
rc := waitForContainer(t, cli, id, 5)
|
||||
rc := waitForContainer(t, cli, id, 10*time.Second)
|
||||
if rc != 1 {
|
||||
t.Errorf("Expected rc=1, got rc=%v", rc)
|
||||
}
|
||||
@@ -164,12 +164,12 @@ func TestSecurityVulnerabilitiesRedHat(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
mnt = strings.TrimSpace(mnt)
|
||||
_, _, err = command.Run("bash", "-c", "cp /etc/yum.repos.d/* "+ filepath.Join(mnt, "/etc/yum.repos.d/"))
|
||||
_, _, err = command.Run("bash", "-c", "cp /etc/yum.repos.d/* "+filepath.Join(mnt, "/etc/yum.repos.d/"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
out, ret, _ := command.Run("bash", "-c", "yum --installroot="+mnt+" updateinfo list sec | grep /Sec")
|
||||
if ret != 1{
|
||||
if ret != 1 {
|
||||
t.Errorf("Expected no vulnerabilities, found the following:\n%v", out)
|
||||
}
|
||||
}
|
||||
@@ -279,6 +279,70 @@ func TestNoVolumeWithRestart(t *testing.T) {
|
||||
waitForReady(t, cli, id)
|
||||
}
|
||||
|
||||
// TestVolumeRequiresRoot tests the case where only the root user can write
|
||||
// to the persistent volume. In this case, an "init container" is needed,
|
||||
// where `runmqserver -i` is run to initialize the storage. Then the
|
||||
// container can be run as normal.
|
||||
func TestVolumeRequiresRoot(t *testing.T) {
|
||||
cli, err := client.NewEnvClient()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
vol := createVolume(t, cli)
|
||||
defer removeVolume(t, cli, vol.Name)
|
||||
|
||||
// Set permissions on the volume to only allow root to write it
|
||||
// It's important that read and execute permissions are given to other users
|
||||
rc, _ := runContainerOneShotWithVolume(t, cli, vol.Name+":/mnt/mqm:nocopy", "bash", "-c", "chown 65534:4294967294 /mnt/mqm/ && chmod 0755 /mnt/mqm/ && ls -lan /mnt/mqm/")
|
||||
if rc != 0 {
|
||||
t.Errorf("Expected one shot container to return rc=0, got rc=%v", rc)
|
||||
}
|
||||
|
||||
containerConfig := container.Config{
|
||||
Image: imageName(),
|
||||
Env: []string{"LICENSE=accept", "MQ_QMGR_NAME=qm1"},
|
||||
}
|
||||
hostConfig := container.HostConfig{
|
||||
Binds: []string{
|
||||
coverageBind(t),
|
||||
vol.Name + ":/mnt/mqm:nocopy",
|
||||
},
|
||||
}
|
||||
networkingConfig := network.NetworkingConfig{}
|
||||
|
||||
// Run an "init container" as root, with the "-i" option, to initialize the volume
|
||||
containerConfig = container.Config{
|
||||
Image: imageName(),
|
||||
Env: []string{"LICENSE=accept", "MQ_QMGR_NAME=qm1", "DEBUG=true"},
|
||||
User: "0",
|
||||
Entrypoint: []string{"runmqserver", "-i"},
|
||||
}
|
||||
initCtr, err := cli.ContainerCreate(context.Background(), &containerConfig, &hostConfig, &networkingConfig, t.Name()+"Init")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer cleanContainer(t, cli, initCtr.ID)
|
||||
t.Logf("Init container ID=%v", initCtr.ID)
|
||||
startContainer(t, cli, initCtr.ID)
|
||||
rc = waitForContainer(t, cli, initCtr.ID, 10*time.Second)
|
||||
if rc != 0 {
|
||||
t.Errorf("Expected init container to exit with rc=0, got rc=%v", rc)
|
||||
}
|
||||
|
||||
containerConfig = container.Config{
|
||||
Image: imageName(),
|
||||
Env: []string{"LICENSE=accept", "MQ_QMGR_NAME=qm1", "DEBUG=true"},
|
||||
}
|
||||
ctr, err := cli.ContainerCreate(context.Background(), &containerConfig, &hostConfig, &networkingConfig, t.Name()+"Main")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer cleanContainer(t, cli, ctr.ID)
|
||||
t.Logf("Main container ID=%v", ctr.ID)
|
||||
startContainer(t, cli, ctr.ID)
|
||||
waitForReady(t, cli, ctr.ID)
|
||||
}
|
||||
|
||||
// TestCreateQueueManagerFail causes a failure of `crtmqm`
|
||||
func TestCreateQueueManagerFail(t *testing.T) {
|
||||
t.Parallel()
|
||||
@@ -286,24 +350,31 @@ func TestCreateQueueManagerFail(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
img, _, err := cli.ImageInspectWithRaw(context.Background(), imageName())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
var files = []struct {
|
||||
Name, Body string
|
||||
}{
|
||||
{"Dockerfile", fmt.Sprintf(`
|
||||
FROM %v
|
||||
USER root
|
||||
RUN echo '#!/bin/bash\nexit 999' > /opt/mqm/bin/crtmqm
|
||||
RUN chown mqm:mqm /opt/mqm/bin/crtmqm
|
||||
RUN chmod 6550 /opt/mqm/bin/crtmqm
|
||||
USER mqm`, imageName())},
|
||||
}
|
||||
oldEntrypoint := strings.Join(img.Config.Entrypoint, " ")
|
||||
tag := createImage(t, cli, files)
|
||||
defer deleteImage(t, cli, tag)
|
||||
|
||||
containerConfig := container.Config{
|
||||
Env: []string{"LICENSE=accept", "MQ_QMGR_NAME=qm1"},
|
||||
// Override the entrypoint to create the queue manager directory, but leave it empty.
|
||||
// This will cause `crtmqm` to return with an exit code of 2.
|
||||
Entrypoint: []string{"bash", "-c", "mkdir -p /mnt/mqm/data && mkdir -p /var/mqm/qmgrs/qm1 && exec " + oldEntrypoint},
|
||||
Env: []string{"LICENSE=accept", "MQ_QMGR_NAME=qm1"},
|
||||
Image: tag,
|
||||
}
|
||||
id := runContainer(t, cli, &containerConfig)
|
||||
defer cleanContainer(t, cli, id)
|
||||
rc := waitForContainer(t, cli, id, 10)
|
||||
rc := waitForContainer(t, cli, id, 10*time.Second)
|
||||
if rc != 1 {
|
||||
t.Errorf("Expected rc=1, got rc=%v", rc)
|
||||
}
|
||||
expectTerminationMessage(t)
|
||||
expectTerminationMessage(t, cli, id)
|
||||
}
|
||||
|
||||
// TestStartQueueManagerFail causes a failure of `strmqm`
|
||||
@@ -313,24 +384,31 @@ func TestStartQueueManagerFail(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
img, _, err := cli.ImageInspectWithRaw(context.Background(), imageName())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
var files = []struct {
|
||||
Name, Body string
|
||||
}{
|
||||
{"Dockerfile", fmt.Sprintf(`
|
||||
FROM %v
|
||||
USER root
|
||||
RUN echo '#!/bin/bash\ndltmqm $@ && strmqm $@' > /opt/mqm/bin/strmqm
|
||||
RUN chown mqm:mqm /opt/mqm/bin/strmqm
|
||||
RUN chmod 6550 /opt/mqm/bin/strmqm
|
||||
USER mqm`, imageName())},
|
||||
}
|
||||
oldEntrypoint := strings.Join(img.Config.Entrypoint, " ")
|
||||
tag := createImage(t, cli, files)
|
||||
defer deleteImage(t, cli, tag)
|
||||
|
||||
containerConfig := container.Config{
|
||||
Env: []string{"LICENSE=accept", "MQ_QMGR_NAME=qm1", "DEBUG=1"},
|
||||
// Override the entrypoint to replace `strmqm` with a script which deletes the queue manager.
|
||||
// This will cause `strmqm` to return with an exit code of 72.
|
||||
Entrypoint: []string{"bash", "-c", "echo '#!/bin/bash\ndltmqm $@ && strmqm $@' > /opt/mqm/bin/strmqm && exec " + oldEntrypoint},
|
||||
Env: []string{"LICENSE=accept", "MQ_QMGR_NAME=qm1"},
|
||||
Image: tag,
|
||||
}
|
||||
id := runContainer(t, cli, &containerConfig)
|
||||
defer cleanContainer(t, cli, id)
|
||||
rc := waitForContainer(t, cli, id, 10)
|
||||
rc := waitForContainer(t, cli, id, 10*time.Second)
|
||||
if rc != 1 {
|
||||
t.Errorf("Expected rc=1, got rc=%v", rc)
|
||||
}
|
||||
expectTerminationMessage(t)
|
||||
expectTerminationMessage(t, cli, id)
|
||||
}
|
||||
|
||||
// TestVolumeUnmount runs a queue manager with a volume, and then forces an
|
||||
@@ -430,7 +508,13 @@ func TestMQSC(t *testing.T) {
|
||||
var files = []struct {
|
||||
Name, Body string
|
||||
}{
|
||||
{"Dockerfile", fmt.Sprintf("FROM %v\nRUN rm -f /etc/mqm/*.mqsc\nADD test.mqsc /etc/mqm/", imageName())},
|
||||
{"Dockerfile", fmt.Sprintf(`
|
||||
FROM %v
|
||||
USER root
|
||||
RUN rm -f /etc/mqm/*.mqsc
|
||||
ADD test.mqsc /etc/mqm/
|
||||
RUN chmod 0660 /etc/mqm/test.mqsc
|
||||
USER mqm`, imageName())},
|
||||
{"test.mqsc", "DEFINE QLOCAL(test)"},
|
||||
}
|
||||
tag := createImage(t, cli, files)
|
||||
@@ -461,7 +545,13 @@ func TestInvalidMQSC(t *testing.T) {
|
||||
var files = []struct {
|
||||
Name, Body string
|
||||
}{
|
||||
{"Dockerfile", fmt.Sprintf("FROM %v\nRUN rm -f /etc/mqm/*.mqsc\nADD mqscTest.mqsc /etc/mqm/", imageName())},
|
||||
{"Dockerfile", fmt.Sprintf(`
|
||||
FROM %v
|
||||
USER root
|
||||
RUN rm -f /etc/mqm/*.mqsc
|
||||
ADD mqscTest.mqsc /etc/mqm/
|
||||
RUN chmod 0660 /etc/mqm/mqscTest.mqsc
|
||||
USER mqm`, imageName())},
|
||||
{"mqscTest.mqsc", "DEFINE INVALIDLISTENER('TEST.LISTENER.TCP') TRPTYPE(TCP) PORT(1414) CONTROL(QMGR) REPLACE"},
|
||||
}
|
||||
tag := createImage(t, cli, files)
|
||||
@@ -473,11 +563,11 @@ func TestInvalidMQSC(t *testing.T) {
|
||||
}
|
||||
id := runContainer(t, cli, &containerConfig)
|
||||
defer cleanContainer(t, cli, id)
|
||||
rc := waitForContainer(t, cli, id, 5)
|
||||
rc := waitForContainer(t, cli, id, 60*time.Second)
|
||||
if rc != 1 {
|
||||
t.Errorf("Expected rc=1, got rc=%v", rc)
|
||||
}
|
||||
expectTerminationMessage(t)
|
||||
expectTerminationMessage(t, cli, id)
|
||||
}
|
||||
|
||||
// TestReadiness creates a new image with large amounts of MQSC in, to
|
||||
@@ -497,7 +587,13 @@ func TestReadiness(t *testing.T) {
|
||||
var files = []struct {
|
||||
Name, Body string
|
||||
}{
|
||||
{"Dockerfile", fmt.Sprintf("FROM %v\nRUN rm -f /etc/mqm/*.mqsc\nADD test.mqsc /etc/mqm/", imageName())},
|
||||
{"Dockerfile", fmt.Sprintf(`
|
||||
FROM %v
|
||||
USER root
|
||||
RUN rm -f /etc/mqm/*.mqsc
|
||||
ADD test.mqsc /etc/mqm/
|
||||
RUN chmod 0660 /etc/mqm/test.mqsc
|
||||
USER mqm`, imageName())},
|
||||
{"test.mqsc", buf.String()},
|
||||
}
|
||||
tag := createImage(t, cli, files)
|
||||
@@ -656,11 +752,11 @@ func TestBadLogFormat(t *testing.T) {
|
||||
}
|
||||
id := runContainer(t, cli, &containerConfig)
|
||||
defer cleanContainer(t, cli, id)
|
||||
rc := waitForContainer(t, cli, id, 5)
|
||||
rc := waitForContainer(t, cli, id, 5*time.Second)
|
||||
if rc != 1 {
|
||||
t.Errorf("Expected rc=1, got rc=%v", rc)
|
||||
}
|
||||
expectTerminationMessage(t)
|
||||
expectTerminationMessage(t, cli, id)
|
||||
}
|
||||
|
||||
// TestMQJSONDisabled tests the case where MQ's JSON logging feature is
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2017, 2018
|
||||
© Copyright IBM Corporation 2017, 2019
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -59,6 +59,18 @@ func imageNameDevJMS() string {
|
||||
return image
|
||||
}
|
||||
|
||||
func baseImage(t *testing.T, cli *client.Client) string {
|
||||
rc, out := runContainerOneShot(t, cli, "bash", "-c", "cat /etc/*release | grep \"^ID=\"")
|
||||
if rc != 0 {
|
||||
t.Fatal("Couldn't determine base image")
|
||||
}
|
||||
s := strings.Split(out, "=")
|
||||
if len(s) < 2 {
|
||||
t.Fatal("Couldn't determine base image string")
|
||||
}
|
||||
return s[1]
|
||||
}
|
||||
|
||||
// isWSL return whether we are running in the Windows Subsystem for Linux
|
||||
func isWSL(t *testing.T) bool {
|
||||
if runtime.GOOS == "linux" {
|
||||
@@ -124,46 +136,31 @@ func getTempDir(t *testing.T, unixStylePath bool) string {
|
||||
return "/tmp/"
|
||||
}
|
||||
|
||||
// terminationLogUnixPath returns the name of the file to use for the termination log message, with a UNIX path
|
||||
func terminationLogUnixPath(t *testing.T) string {
|
||||
// Warning: this directory must be accessible to the Docker daemon,
|
||||
// in order to enable the bind mount
|
||||
return getTempDir(t, true) + t.Name() + "-termination-log"
|
||||
}
|
||||
|
||||
// terminationLogOSPath returns the name of the file to use for the termination log message, with an OS specific path
|
||||
func terminationLogOSPath(t *testing.T) string {
|
||||
// Warning: this directory must be accessible to the Docker daemon,
|
||||
// in order to enable the bind mount
|
||||
return getTempDir(t, false) + t.Name() + "-termination-log"
|
||||
}
|
||||
|
||||
// terminationBind returns a string to use to bind-mount a termination log file.
|
||||
// This is done using a bind, because you can't copy files from /dev out of the container.
|
||||
func terminationBind(t *testing.T) string {
|
||||
n := terminationLogUnixPath(t)
|
||||
// Remove it if it already exists
|
||||
os.Remove(n)
|
||||
// Create the empty file
|
||||
f, err := os.OpenFile(n, os.O_WRONLY|os.O_CREATE, 0600)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
f.Close()
|
||||
return terminationLogOSPath(t) + ":/dev/termination-log"
|
||||
}
|
||||
|
||||
// terminationMessage return the termination message, or an empty string if not set
|
||||
func terminationMessage(t *testing.T) string {
|
||||
b, err := ioutil.ReadFile(terminationLogUnixPath(t))
|
||||
func terminationMessage(t *testing.T, cli *client.Client, ID string) string {
|
||||
r, _, err := cli.CopyFromContainer(context.Background(), ID, "/run/termination-log")
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
return ""
|
||||
}
|
||||
return string(b)
|
||||
b, err := ioutil.ReadAll(r)
|
||||
tr := tar.NewReader(bytes.NewReader(b))
|
||||
_, err = tr.Next()
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
return ""
|
||||
}
|
||||
// read the complete content of the file h.Name into the bs []byte
|
||||
content, err := ioutil.ReadAll(tr)
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
return ""
|
||||
}
|
||||
return string(content)
|
||||
}
|
||||
|
||||
func expectTerminationMessage(t *testing.T) {
|
||||
m := terminationMessage(t)
|
||||
func expectTerminationMessage(t *testing.T, cli *client.Client, ID string) {
|
||||
m := terminationMessage(t, cli, ID)
|
||||
if m == "" {
|
||||
t.Error("Expected termination message to be set")
|
||||
}
|
||||
@@ -195,11 +192,10 @@ func cleanContainer(t *testing.T, cli *client.Client, ID string) {
|
||||
// Log the container output for any container we're about to delete
|
||||
t.Logf("Console log from container %v:\n%v", ID, inspectTextLogs(t, cli, ID))
|
||||
|
||||
m := terminationMessage(t)
|
||||
m := terminationMessage(t, cli, ID)
|
||||
if m != "" {
|
||||
t.Logf("Termination message: %v", m)
|
||||
}
|
||||
os.Remove(terminationLogUnixPath(t))
|
||||
|
||||
t.Logf("Removing container: %s", ID)
|
||||
opts := types.ContainerRemoveOptions{
|
||||
@@ -212,6 +208,22 @@ 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.
|
||||
@@ -219,15 +231,35 @@ func runContainerWithPorts(t *testing.T, cli *client.Client, containerConfig *co
|
||||
if containerConfig.Image == "" {
|
||||
containerConfig.Image = imageName()
|
||||
}
|
||||
// Always run as the "mqm" user, unless the test has specified otherwise
|
||||
if containerConfig.User == "" {
|
||||
containerConfig.User = "mqm"
|
||||
}
|
||||
// if coverage
|
||||
containerConfig.Env = append(containerConfig.Env, "COVERAGE_FILE="+t.Name()+".cov")
|
||||
containerConfig.Env = append(containerConfig.Env, "EXIT_CODE_FILE="+getExitCodeFilename(t))
|
||||
hostConfig := container.HostConfig{
|
||||
Binds: []string{
|
||||
coverageBind(t),
|
||||
terminationBind(t),
|
||||
// terminationBind(t),
|
||||
},
|
||||
PortBindings: nat.PortMap{},
|
||||
CapDrop: []string{
|
||||
"ALL",
|
||||
},
|
||||
}
|
||||
if devImage(t, cli, containerConfig.Image) {
|
||||
t.Logf("Detected MQ Advanced for Developers image — adding extra Linux capabilities to container")
|
||||
hostConfig.CapAdd = []string{
|
||||
"CHOWN",
|
||||
"SETUID",
|
||||
"SETGID",
|
||||
"AUDIT_WRITE",
|
||||
}
|
||||
// Only needed for a RHEL-based image
|
||||
if baseImage(t, cli) != "ubuntu" {
|
||||
hostConfig.CapAdd = append(hostConfig.CapAdd, "DAC_OVERRIDE")
|
||||
}
|
||||
}
|
||||
for _, p := range ports {
|
||||
port := nat.Port(fmt.Sprintf("%v/tcp", p))
|
||||
@@ -254,13 +286,49 @@ func runContainer(t *testing.T, cli *client.Client, containerConfig *container.C
|
||||
return runContainerWithPorts(t, cli, containerConfig, nil)
|
||||
}
|
||||
|
||||
// runContainerOneShot runs a container with a custom entrypoint, as the root
|
||||
// user and with default capabilities
|
||||
func runContainerOneShot(t *testing.T, cli *client.Client, command ...string) (int64, string) {
|
||||
containerConfig := container.Config{
|
||||
Entrypoint: command,
|
||||
User: "root",
|
||||
Image: imageName(),
|
||||
}
|
||||
id := runContainer(t, cli, &containerConfig)
|
||||
defer cleanContainer(t, cli, id)
|
||||
return waitForContainer(t, cli, id, 10), inspectLogs(t, cli, id)
|
||||
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())
|
||||
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)
|
||||
}
|
||||
|
||||
// runContainerOneShot runs a container with a custom entrypoint, as the root
|
||||
// user, with default capabilities, and a volume mounted
|
||||
func runContainerOneShotWithVolume(t *testing.T, cli *client.Client, bind string, command ...string) (int64, string) {
|
||||
containerConfig := container.Config{
|
||||
Entrypoint: command,
|
||||
User: "root",
|
||||
Image: imageName(),
|
||||
}
|
||||
hostConfig := container.HostConfig{
|
||||
Binds: []string{
|
||||
bind,
|
||||
},
|
||||
}
|
||||
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())
|
||||
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)
|
||||
}
|
||||
|
||||
func startContainer(t *testing.T, cli *client.Client, ID string) {
|
||||
@@ -309,19 +377,19 @@ func getCoverageExitCode(t *testing.T, orig int64) int64 {
|
||||
}
|
||||
|
||||
// waitForContainer waits until a container has exited
|
||||
func waitForContainer(t *testing.T, cli *client.Client, ID string, timeout int64) int64 {
|
||||
rc, err := cli.ContainerWait(context.Background(), ID)
|
||||
|
||||
func waitForContainer(t *testing.T, cli *client.Client, ID string, timeout time.Duration) int64 {
|
||||
c, cancel := context.WithTimeout(context.Background(), timeout)
|
||||
defer cancel()
|
||||
rc, err := cli.ContainerWait(c, ID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if coverage() {
|
||||
// COVERAGE: When running coverage, the exit code is written to a file,
|
||||
// to allow the coverage to be generated (which doesn't happen for non-zero
|
||||
// exit codes)
|
||||
rc = getCoverageExitCode(t, rc)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return rc
|
||||
}
|
||||
|
||||
@@ -395,7 +463,7 @@ func execContainer(t *testing.T, cli *client.Client, ID string, user string, cmd
|
||||
}
|
||||
|
||||
func waitForReady(t *testing.T, cli *client.Client, ID string) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
|
||||
defer cancel()
|
||||
|
||||
for {
|
||||
|
||||
Reference in New Issue
Block a user