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