I’m having an issue using docker.io/go-docker
with mongo --eval
and…I’m missing something.
The idea here is that we spawn a container running mongo (which works), and execute a mongo command within the container once it’s online. I can get basic commands to execute with this function (e.g. echo something) and I’m able to see stdout/stderr from the CLI, but I can’t see the actual traceback from the mongo command itself. It looks like that my code is properly connecting to the mongo process in the container, but doesn’t actually execute the `–eval- command. I’ve got an example here - Go Playground - The Go Programming Language - with all the current traceback in the function header comments.
Any ideas? I think I must be missing something with how --eval works.
Code included here because the go.dev link will expire:
package main
import (
"context"
"errors"
"fmt"
"io"
"docker.io/go-docker/api/types"
"github.com/sirupsen/logrus"
)
// ExecCommandInMongoContainer attaches to the mongo container and executes the provided command
// In the case that an error occurs either spawning the docker context or executing the command, an error
// will be returned. In the case that an error is returned from a malformed/bad command, then output
// is also populated.
//
// Traceback on mongo 'success' - NOTE the lack of information returned - it should be printing a JSON object
// time="2022-07-19T14:12:22-06:00" level=info msg="Successfully spawned mongo docker test container." containerID=276390114f752d8bc450e4e4eeeb2c44331ae684b8ad70fd7f20b57025fbfb37 containerMongoPort=64709 containerName=mongo-64709 src=mongotest.TestConnection
// Executing in container 276390114f752d8bc450e4e4eeeb2c44331ae684b8ad70fd7f20b57025fbfb37 -
// mongo --eval 'var res = rs.initiate({"_id": "cf", "members": [{"_id": 0, "host": "localhost:27017"}]}); print(tojson(res));'
// --------------------
// MongoDB shell version v5.0.9
// connecting to: mongodb://127.0.0.1:27017/?compressors=disabled&gssapiServiceName=mongodb
// Implicit session: session { "id" : UUID("96baebe3-307c-4ba7-bc7d-40cc371b3949") }
// MongoDB server version: 5.0.9
// var res = rs.initiate({"_id": "cf", "members": [{"_id": 0, "host": "localhost:27017"}]}); print(tojson(res));
//
// --------------------
//
// Traceback on mongo failure - NOTE again the lack of debug when I intentionally throw a bad mongo command in here
// Executing in container e952b357a6ca9d6ecfeaa5547ad0a79fee25899fff25e3a71b2166c2f82cf5d8 -
// mongo --eval 'printjson(INVALID.status())'
// --------------------
// MongoDB shell version v5.0.9
// connecting to: mongodb://127.0.0.1:27017/?compressors=disabled&gssapiServiceName=mongodb
// Implicit session: session { "id" : UUID("8e1e4346-0b19-4fd4-a4a8-43d276296710") }
// MongoDB server version: 5.0.9
// printjson(INVALID.status())
//
// --------------------
//
// Traceback on the command failing itself - NOTE that this is correctly rendering and returning
// Executing in container 2f30359c9c208a5995b7e814f9c1ca486046ee4bbfa252820cafa0711046fa1a -
// notAValidCommand --eval 'printjson(rs.status())'
// --------------------
// OCI runtime exec failed: exec failed: container_linux.go:380: starting container process caused: exec: "notAValidCommand": executable file not found in $PATH: unknown
//
// --------------------
func (tc *TestConnection) ExecCommandInMongoContainer(cmd []string) (output string, err error) {
execOpts := types.ExecConfig{
Privileged: false,
Tty: true,
AttachStdin: true,
AttachStderr: true,
AttachStdout: true,
Cmd: cmd,
}
execIDObj, err := tc.dockerClient.ContainerExecCreate(context.Background(), tc.mongoContainerID, execOpts)
if err != nil {
err = fmt.Errorf("could not create execution context for container %s: %w", tc.mongoContainerID, err)
tc.logger.WithFields(logrus.Fields{
"err": err,
"cmd": cmd,
}).Error("Could not create execution context for provided command")
return output, err
}
// Kick off the command and attach to the container - it will return a reader object we can read from
attachedRes, err := tc.dockerClient.ContainerExecAttach(context.Background(), execIDObj.ID, execOpts)
if err != nil {
// Error attaching to container - we won't get an
err = fmt.Errorf("could not attach to execution context for container %s: %w", tc.mongoContainerID, err)
tc.logger.WithFields(logrus.Fields{
"err": err,
"cmd": cmd,
}).Error("Could not attach to execution context for provided container")
return output, err
}
defer attachedRes.Close()
resultsExist := true
msg := fmt.Sprintf("Executing in container %s - \n\t", tc.mongoContainerID)
for _, c := range cmd {
msg += c + " "
}
output += msg + "\n"
output += "--------------------\n"
results := ""
for resultsExist {
// Walk through the results - could be successful or not
line, _, err := attachedRes.Reader.ReadLine()
if err != nil {
if errors.Is(err, io.EOF) {
// No more results from command - break out of the loop
resultsExist = false
break
}
err = fmt.Errorf("could not read lines from container '%s': %w", tc.mongoContainerID, err)
tc.logger.WithFields(logrus.Fields{
"err": err,
"cmd": cmd,
}).Error("Could not execute provided command")
return output, err
}
results += string(line) + "\n"
}
output += results + "\n"
output += "--------------------\n"
inspectRes, err := tc.dockerClient.ContainerExecInspect(context.Background(), execIDObj.ID)
if err != nil {
err = fmt.Errorf("could not inspect command execution in container %s: %w", tc.mongoContainerID, err)
tc.logger.WithFields(logrus.Fields{
"err": err,
"cmd": cmd,
}).Error("Could not inspect containter after executing provided command")
return output, err
}
if inspectRes.ExitCode > 0 {
// The command itself returned a bad exit code (e.g. malformed command)
err = fmt.Errorf("could not execute provided command in container %s: \n%s", tc.mongoContainerID, results)
tc.logger.WithFields(logrus.Fields{
"err": err,
"cmd": cmd,
}).Error("There was an error executing the provided command")
}
return output, err
}
func main() {
fmt.Println("Hello, 世界")
}