github.com/rvaralda/deis@v1.4.1/tests/dockercli/dockercli.go (about) 1 // Package dockercli provides helper functions for testing with Docker. 2 package dockercli 3 4 import ( 5 "bufio" 6 "crypto/tls" 7 "fmt" 8 "io" 9 "log" 10 "net" 11 "net/http" 12 "net/url" 13 "os" 14 "path/filepath" 15 "strings" 16 "testing" 17 "time" 18 19 "github.com/deis/deis/tests/utils" 20 "github.com/docker/docker/api/client" 21 ) 22 23 const ( 24 defaultKeyFile = "key.pem" 25 defaultCertFile = "cert.pem" 26 ) 27 28 // CloseWrap ensures that an io.Writer is closed. 29 func CloseWrap(args ...io.Closer) error { 30 e := false 31 ret := fmt.Errorf("Error closing elements") 32 for _, c := range args { 33 if err := c.Close(); err != nil { 34 e = true 35 ret = fmt.Errorf("%s\n%s", ret, err) 36 } 37 } 38 if e { 39 return ret 40 } 41 return nil 42 } 43 44 // DeisServiceTest tries to connect to a container and port using the 45 // specified protocol. 46 func DeisServiceTest( 47 t *testing.T, container string, port string, protocol string) { 48 ipaddr := utils.HostAddress() 49 if ipaddr == "" { 50 ipaddr = GetInspectData( 51 t, "{{ .NetworkSettings.ipaddr }}", container) 52 } 53 fmt.Println("Running service test for " + container) 54 if strings.Contains(ipaddr, "Error") { 55 t.Fatalf("wrong IP %s", ipaddr) 56 } 57 if protocol == "http" { 58 url := "http://" + ipaddr + ":" + port 59 response, err := http.Get(url) 60 if err != nil { 61 t.Fatalf("Not reachable %s", err) 62 } 63 fmt.Println(response) 64 } 65 if protocol == "tcp" || protocol == "udp" { 66 conn, err := net.Dial(protocol, ipaddr+":"+port) 67 if err != nil { 68 t.Fatalf("Not reachable %s", err) 69 } 70 _, err = conn.Write([]byte("HEAD")) 71 if err != nil { 72 t.Fatalf("Not reachable %s", err) 73 } 74 } 75 } 76 77 // DockerHost returns the protocol and address of the docker server. 78 func DockerHost() (string, string, error) { 79 dockerHost := os.Getenv("DOCKER_HOST") 80 if dockerHost == "" { 81 dockerHost = "unix:///var/run/docker.sock" 82 } 83 u, err := url.Parse(dockerHost) 84 if err != nil { 85 return "", "", err 86 } 87 if u.Scheme == "unix" { 88 return u.Scheme, u.Path, nil 89 } 90 return u.Scheme, u.Host, nil 91 } 92 93 // NewClient returns a new docker test client. 94 func NewClient() ( 95 cli *client.DockerCli, stdout *io.PipeReader, stdoutPipe *io.PipeWriter) { 96 proto, addr, _ := DockerHost() 97 stdout, stdoutPipe = io.Pipe() 98 99 dockerCertPath := os.Getenv("DOCKER_CERT_PATH") 100 // Boot2docker use TLS per default, Jenkins not 101 if dockerCertPath != "" { 102 var ( 103 tlsConfig tls.Config 104 ) 105 tlsConfig.InsecureSkipVerify = true 106 107 flCert := filepath.Join(dockerCertPath, defaultCertFile) 108 flKey := filepath.Join(dockerCertPath, defaultKeyFile) 109 110 _, errCert := os.Stat(flCert) 111 _, errKey := os.Stat(flKey) 112 if errCert == nil && errKey == nil { 113 cert, err := tls.LoadX509KeyPair(flCert, flKey) 114 if err != nil { 115 log.Fatalf("Couldn't load X509 key pair: %s. Key encrypted?", err) 116 } 117 tlsConfig.Certificates = []tls.Certificate{cert} 118 } 119 // Avoid fallback to SSL protocols < TLS1.0 120 tlsConfig.MinVersion = tls.VersionTLS10 121 cli = client.NewDockerCli(nil, stdoutPipe, nil, nil, proto, addr, &tlsConfig) 122 } else { 123 cli = client.NewDockerCli(nil, stdoutPipe, nil, nil, proto, addr, nil) 124 } 125 return 126 } 127 128 // PrintToStdout prints a string to stdout. 129 func PrintToStdout(t *testing.T, stdout *io.PipeReader, 130 stdoutPipe *io.PipeWriter, stoptag string) string { 131 var result string 132 r := bufio.NewReader(stdout) 133 for { 134 cmdBytes, err := r.ReadString('\n') 135 if err != nil { 136 break 137 } 138 result = cmdBytes 139 fmt.Print(cmdBytes) 140 if strings.Contains(cmdBytes, stoptag) == true { 141 if err := CloseWrap(stdout, stdoutPipe); err != nil { 142 t.Fatal(err) 143 } 144 } 145 } 146 return result 147 } 148 149 // GetInspectData prints and returns `docker inspect` data for a container. 150 func GetInspectData(t *testing.T, format string, container string) string { 151 var inspectData string 152 cli, stdout, stdoutPipe := NewClient() 153 fmt.Println("Getting inspect data :" + format + ":" + container) 154 go func() { 155 err := cli.CmdInspect("--format", format, container) 156 if err != nil { 157 fmt.Printf("%s %s", format, err) 158 } 159 if err = CloseWrap(stdout, stdoutPipe); err != nil { 160 t.Fatalf("inspect data failed %s", err) 161 } 162 }() 163 go func() { 164 time.Sleep(3000 * time.Millisecond) 165 if err := CloseWrap(stdout, stdoutPipe); err != nil { 166 t.Fatalf("Inspect data %s", err) 167 } 168 }() 169 time.Sleep(1000 * time.Millisecond) 170 inspectData = PrintToStdout(t, stdout, stdoutPipe, "get inspect data") 171 return strings.TrimSuffix(inspectData, "\n") 172 } 173 174 // RunContainer runs a docker image with the given arguments. 175 func RunContainer(cli *client.DockerCli, args ...string) error { 176 // fmt.Println("--- Run docker container", args[1]) 177 err := cli.CmdRun(args...) 178 if err != nil { 179 // Ignore certain errors we see in io handling. 180 switch msg := err.Error(); { 181 case strings.Contains(msg, "read/write on closed pipe"): 182 err = nil 183 case strings.Contains(msg, "Code: -1"): 184 err = nil 185 case strings.Contains(msg, "Code: 2"): 186 err = nil 187 } 188 } 189 return err 190 } 191 192 // RunDeisDataTest starts a data container as a prerequisite for a service. 193 func RunDeisDataTest(t *testing.T, args ...string) { 194 done := make(chan bool, 1) 195 cli, stdout, stdoutPipe := NewClient() 196 var hostname string 197 fmt.Println(args[2] + " test") 198 hostname = GetInspectData(t, "{{ .Config.Hostname }}", args[1]) 199 fmt.Println("data container " + hostname) 200 done <- true 201 if strings.Contains(hostname, "Error") { 202 go func() { 203 <-done 204 if err := RunContainer(cli, args...); err != nil { 205 t.Fatal(err) 206 } 207 }() 208 go func() { 209 time.Sleep(3000 * time.Millisecond) 210 if err := CloseWrap(stdout, stdoutPipe); err != nil { 211 t.Fatalf("Inspect Element %s", err) 212 } 213 }() 214 PrintToStdout(t, stdout, stdoutPipe, "running"+args[1]) 215 } 216 } 217 218 // GetImageID returns the ID of a docker image. 219 func GetImageID(t *testing.T, repo string) string { 220 var imageID string 221 cli, stdout, stdoutPipe := NewClient() 222 go func() { 223 err := cli.CmdImages() 224 if err != nil { 225 t.Fatalf("GetImageID %s", err) 226 } 227 if err = CloseWrap(stdout, stdoutPipe); err != nil { 228 t.Fatalf("GetImageID %s", err) 229 } 230 }() 231 imageID = PrintToStdout(t, stdout, stdoutPipe, repo) 232 return strings.Fields(imageID)[2] 233 } 234 235 // RunTestEtcd starts an etcd docker container for testing. 236 func RunTestEtcd(t *testing.T, name string, port string) { 237 var err error 238 cli, stdout, stdoutPipe := NewClient() 239 etcdImage := "deis/test-etcd:latest" 240 ipaddr := utils.HostAddress() 241 etcdAddr := ipaddr + ":" + port 242 fmt.Printf("--- Running deis/test-etcd at %s\n", etcdAddr) 243 done2 := make(chan bool, 1) 244 go func() { 245 done2 <- true 246 _ = cli.CmdRm("-f", name) 247 err = RunContainer(cli, 248 "--name", name, 249 "--rm", 250 "-p", port+":"+port, 251 "-e", "HOST_IP="+ipaddr, 252 "-e", "ETCD_ADDR="+etcdAddr, 253 etcdImage) 254 }() 255 go func() { 256 <-done2 257 time.Sleep(5000 * time.Millisecond) 258 if err := CloseWrap(stdout, stdoutPipe); err != nil { 259 t.Fatalf("runEtcdTest %s", err) 260 } 261 }() 262 time.Sleep(1000 * time.Millisecond) 263 PrintToStdout(t, stdout, stdoutPipe, "pulling etcd") 264 if err != nil { 265 t.Fatal(err) 266 } 267 }