@@ -8,6 +8,7 @@ package mongotest
88import (
99 "context"
1010 "fmt"
11+ "io/ioutil"
1112 "net"
1213 "runtime/debug"
1314 "strconv"
@@ -31,9 +32,15 @@ type TestConnection struct {
3132}
3233
3334// NewTestConnection is the standard method for initializing a TestConnection - it has a side-effect
34- // of spawning a new docker container
35+ // of spawning a new docker container if spinupDockerContainer is set to true.
36+ // Note that the first time this is called on a new system, the mongo docker
37+ // container will be pulled. Any subsequent calls on the system should succeed without
38+ // calls to pull.
39+ // If spinupDockerContainer is False, then no docker shenanigans occur, instead
40+ // an attempt is made to connect to a locally running mongo instance
41+ // (e.g. mongodb://127.0.0.1:27017).
3542func NewTestConnection (spinupDockerContainer bool ) (* TestConnection , error ) {
36- // TODO: How should we be handling logging? What do the base packages do?
43+ // TODO: How should we be handling logging? What do other libraries typically do?
3744 logger := logrus .New ().WithField ("src" , "mongotest.TestConnection" )
3845 mongoURI := "mongodb://127.0.0.1"
3946 testConn := & TestConnection {
@@ -46,9 +53,6 @@ func NewTestConnection(spinupDockerContainer bool) (*TestConnection, error) {
4653 "stack" : string (debug .Stack ()),
4754 }).Error ("A panic occurred when trying to initialize a TestConnection" )
4855 // Initialization crashed - ensure the mongo container is destroyed
49- // if err = testConn.KillMongoContainer(); err != nil {
50- // logger.WithField("err", err).Error("Could not kill mongo container after TestConnection panic")
51- // }
5256 _ = testConn .KillMongoContainer ()
5357 }
5458 }()
@@ -141,31 +145,29 @@ func GetAvailablePort() (port int, err error) {
141145 if err != nil {
142146 return 0 , err
143147 }
144- // Now try to listen/read on it - just for a few tics
145- // Calling this makes osx prompt for firewall/network permission
146- // conn, err := server.Accept()
147- // defer conn.Close()
148- // err = conn.SetReadDeadline(time.Now().Add(time.Minute))
149- // if err != nil {
150- // return 0, err
151- // }
152- // TODO: Fix this check
153- // go func() {
154- // b := []byte{}
155- // _, err = conn.Read(b)
156- // if err != nil {
157- // // return 0, err
158- // panic(err)
159- // }
160- // }()
161- // // Write to connection
162- // _, err = conn.Write([]byte{1})
163148
164149 // Return the port as an int
165150 // TODO: This is used as a string elsewhere - consider string
166151 return strconv .Atoi (portString )
167152}
168153
154+ // pullMongoContainer fetches the mongo container from dockerhub
155+ func (tc * TestConnection ) pullMongoContainer (mongoImageName string ) (err error ) {
156+ // TODO: Is this better to do as an error handler?
157+ // Pull the initial container
158+ tc .logger .Info ("Starting mongo docker image pull" )
159+ rc , err := tc .dockerClient .ImagePull (context .Background (), mongoImageName , types.ImagePullOptions {})
160+ defer rc .Close ()
161+ if err != nil {
162+ return fmt .Errorf ("could not pull mongo container: %v" , err )
163+ }
164+ if _ , err := ioutil .ReadAll (rc ); err != nil {
165+ return fmt .Errorf ("could not pull mongo container: %v" , err )
166+ }
167+ tc .logger .Info ("Done pulling mongo docker image" )
168+ return nil
169+ }
170+
169171// StartMongoContainer starts a mongo docker container
170172// A note that the docker daemon on the system is expected to be running
171173// TODO: Is there a way to spawn the docker daemon myself?
@@ -177,12 +179,7 @@ func (tc *TestConnection) StartMongoContainer(portNumber int) (containerID strin
177179 containerName := fmt .Sprintf ("mongo-%d" , portNumber )
178180
179181 mongoImageName := "registry.hub.docker.com/library/mongo:latest"
180- // TODO: Explicitly pull the initial container - ensure user feedback is in place
181- // rc, err := tc.dockerClient.ImagePull(nil, mongoImageName, types.ImagePullOptions{})
182- // defer rc.Close()
183- // if err != nil {
184182
185- // }
186183 containerResp , err := tc .dockerClient .ContainerCreate (
187184 context .Background (),
188185 & container.Config {
@@ -210,7 +207,16 @@ func (tc *TestConnection) StartMongoContainer(portNumber int) (containerID strin
210207 // TODO: Does this config also need to be specified?
211208 & network.NetworkingConfig {},
212209 containerName )
213- if err != nil {
210+ if err != nil && docker .IsErrNotFound (err ) {
211+ // The image didn't exist locally - go grab it
212+ if err = tc .pullMongoContainer (mongoImageName ); err != nil {
213+ // The pull didn't succeed, bail
214+ tc .logger .WithField ("err" , err ).Error ("Could not pull the docker container" )
215+ return "" , err
216+ }
217+ // Now that the pull is complete, we can try to call start again
218+ return tc .StartMongoContainer (portNumber )
219+ } else if err != nil {
214220 tc .logger .WithField ("err" , err ).Error ("Could not create the docker container" )
215221 return "" , err
216222 }
0 commit comments