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  }