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  }