github.com/cayleygraph/cayley@v0.7.7/internal/dock/dock.go (about) 1 // +build docker 2 3 package dock 4 5 import ( 6 "fmt" 7 "math/rand" 8 "net" 9 "runtime" 10 "strconv" 11 "testing" 12 "time" 13 14 "github.com/fsouza/go-dockerclient" 15 ) 16 17 var ( 18 Address = `unix:///var/run/docker.sock` 19 ) 20 21 type Config struct { 22 docker.Config 23 } 24 25 type fullConfig struct { 26 docker.Config 27 docker.HostConfig 28 } 29 30 func run(t testing.TB, conf fullConfig) (addr string, closer func()) { 31 if testing.Short() { 32 t.SkipNow() 33 } 34 cli, err := docker.NewClient(Address) 35 if err != nil { 36 t.Fatal(err) 37 } 38 39 // If there is not relevant image at local, pull image from remote repository. 40 if err := cli.PullImage( 41 docker.PullImageOptions{ 42 Repository: conf.Image, 43 }, 44 docker.AuthConfiguration{}, 45 ); err != nil { 46 // If pull image fail, skip the test. 47 t.Skip(err) 48 } 49 50 cont, err := cli.CreateContainer(docker.CreateContainerOptions{ 51 Config: &conf.Config, 52 HostConfig: &conf.HostConfig, 53 }) 54 if err != nil { 55 t.Skip(err) 56 } 57 58 closer = func() { 59 cli.RemoveContainer(docker.RemoveContainerOptions{ 60 ID: cont.ID, 61 Force: true, 62 }) 63 } 64 65 if err := cli.StartContainer(cont.ID, &conf.HostConfig); err != nil { 66 closer() 67 t.Skip(err) 68 } 69 70 info, err := cli.InspectContainer(cont.ID) 71 if err != nil { 72 closer() 73 t.Skip(err) 74 } 75 addr = info.NetworkSettings.IPAddress 76 return 77 } 78 79 func randPort() int { 80 const ( 81 min = 10000 82 max = 30000 83 ) 84 for { 85 port := min + rand.Intn(max-min) 86 c, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", localhost, port), time.Second) 87 if c != nil { 88 c.Close() 89 } 90 if err != nil { 91 // TODO: check for a specific error 92 return port 93 } 94 } 95 } 96 97 const localhost = "127.0.0.1" 98 99 func RunAndWait(t testing.TB, conf Config, port string, check func(string) bool) (addr string, closer func()) { 100 fconf := fullConfig{Config: conf.Config} 101 if runtime.GOOS != "linux" { 102 lport := strconv.Itoa(randPort()) 103 // nothing except Linux runs Docker natively, 104 // so we randomize the port and expose it on Docker VM 105 fconf.PortBindings = map[docker.Port][]docker.PortBinding{ 106 docker.Port(port + "/tcp"): {{ 107 HostIP: localhost, 108 HostPort: lport, 109 }}, 110 } 111 port = lport 112 } 113 addr, closer = run(t, fconf) 114 if runtime.GOOS != "linux" { 115 // VM ports are automatically exposed on localhost 116 addr = localhost 117 } 118 addr += ":" + port 119 if check == nil { 120 check = waitPort 121 } 122 ok := false 123 for i := 0; i < 10 && !ok; i++ { 124 ok = check(addr) 125 if !ok { 126 time.Sleep(time.Second * 2) 127 } 128 } 129 if !ok { 130 closer() 131 t.Fatal("Container check fails.") 132 } 133 return addr, closer 134 } 135 136 const wait = time.Second * 5 137 138 func waitPort(addr string) bool { 139 start := time.Now() 140 c, err := net.DialTimeout("tcp", addr, wait) 141 if err == nil { 142 c.Close() 143 } else if dt := time.Since(start); dt < wait { 144 time.Sleep(wait - dt) 145 } 146 return err == nil 147 }