Просмотр исходного кода

Added project deps and scripts

Eugene Yakubovich 10 лет назад
Родитель
Сommit
7361dcdcea
44 измененных файлов с 5512 добавлено и 0 удалено
  1. 28 0
      Godeps/Godeps.json
  2. 5 0
      Godeps/Readme
  3. 2 0
      Godeps/_workspace/.gitignore
  4. 23 0
      Godeps/_workspace/src/github.com/coreos/go-etcd/etcd/add_child.go
  5. 73 0
      Godeps/_workspace/src/github.com/coreos/go-etcd/etcd/add_child_test.go
  6. 435 0
      Godeps/_workspace/src/github.com/coreos/go-etcd/etcd/client.go
  7. 96 0
      Godeps/_workspace/src/github.com/coreos/go-etcd/etcd/client_test.go
  8. 51 0
      Godeps/_workspace/src/github.com/coreos/go-etcd/etcd/cluster.go
  9. 34 0
      Godeps/_workspace/src/github.com/coreos/go-etcd/etcd/compare_and_delete.go
  10. 46 0
      Godeps/_workspace/src/github.com/coreos/go-etcd/etcd/compare_and_delete_test.go
  11. 36 0
      Godeps/_workspace/src/github.com/coreos/go-etcd/etcd/compare_and_swap.go
  12. 57 0
      Godeps/_workspace/src/github.com/coreos/go-etcd/etcd/compare_and_swap_test.go
  13. 55 0
      Godeps/_workspace/src/github.com/coreos/go-etcd/etcd/debug.go
  14. 28 0
      Godeps/_workspace/src/github.com/coreos/go-etcd/etcd/debug_test.go
  15. 40 0
      Godeps/_workspace/src/github.com/coreos/go-etcd/etcd/delete.go
  16. 81 0
      Godeps/_workspace/src/github.com/coreos/go-etcd/etcd/delete_test.go
  17. 48 0
      Godeps/_workspace/src/github.com/coreos/go-etcd/etcd/error.go
  18. 27 0
      Godeps/_workspace/src/github.com/coreos/go-etcd/etcd/get.go
  19. 131 0
      Godeps/_workspace/src/github.com/coreos/go-etcd/etcd/get_test.go
  20. 72 0
      Godeps/_workspace/src/github.com/coreos/go-etcd/etcd/options.go
  21. 377 0
      Godeps/_workspace/src/github.com/coreos/go-etcd/etcd/requests.go
  22. 89 0
      Godeps/_workspace/src/github.com/coreos/go-etcd/etcd/response.go
  23. 42 0
      Godeps/_workspace/src/github.com/coreos/go-etcd/etcd/set_curl_chan_test.go
  24. 137 0
      Godeps/_workspace/src/github.com/coreos/go-etcd/etcd/set_update_create.go
  25. 241 0
      Godeps/_workspace/src/github.com/coreos/go-etcd/etcd/set_update_create_test.go
  26. 3 0
      Godeps/_workspace/src/github.com/coreos/go-etcd/etcd/version.go
  27. 103 0
      Godeps/_workspace/src/github.com/coreos/go-etcd/etcd/watch.go
  28. 119 0
      Godeps/_workspace/src/github.com/coreos/go-etcd/etcd/watch_test.go
  29. 31 0
      Godeps/_workspace/src/github.com/coreos/go-systemd/daemon/sdnotify.go
  30. 2 0
      Godeps/_workspace/src/github.com/docker/libcontainer/netlink/MAINTAINERS
  31. 23 0
      Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink.go
  32. 1002 0
      Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink_linux.go
  33. 9 0
      Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink_linux_arm.go
  34. 11 0
      Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink_linux_notarm.go
  35. 55 0
      Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink_linux_test.go
  36. 76 0
      Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink_unsupported.go
  37. 191 0
      Godeps/_workspace/src/github.com/golang/glog/LICENSE
  38. 44 0
      Godeps/_workspace/src/github.com/golang/glog/README
  39. 1034 0
      Godeps/_workspace/src/github.com/golang/glog/glog.go
  40. 124 0
      Godeps/_workspace/src/github.com/golang/glog/glog_file.go
  41. 333 0
      Godeps/_workspace/src/github.com/golang/glog/glog_test.go
  42. 21 0
      build
  43. 27 0
      cover
  44. 50 0
      test

+ 28 - 0
Godeps/Godeps.json

@@ -0,0 +1,28 @@
+{
+	"ImportPath": "github.com/coreos-inc/kolach",
+	"GoVersion": "go1.2.2",
+	"Packages": [
+		"./..."
+	],
+	"Deps": [
+		{
+			"ImportPath": "github.com/coreos/go-etcd/etcd",
+			"Comment": "v0.2.0-rc1-120-g23142f6",
+			"Rev": "23142f6773a676cc2cae8dd0cb90b2ea761c853f"
+		},
+		{
+			"ImportPath": "github.com/coreos/go-systemd/daemon",
+			"Comment": "v2-26-ga606a1e",
+			"Rev": "a606a1e936df81b70d85448221c7b1c6d8a74ef1"
+		},
+		{
+			"ImportPath": "github.com/docker/libcontainer/netlink",
+			"Comment": "v1.1.0-131-g164cd80",
+			"Rev": "164cd807a16e63ed539cddda55ce3bbc32e1791e"
+		},
+		{
+			"ImportPath": "github.com/golang/glog",
+			"Rev": "d1c4472bf2efd3826f2b5bdcc02d8416798d678c"
+		}
+	]
+}

+ 5 - 0
Godeps/Readme

@@ -0,0 +1,5 @@
+This directory tree is generated automatically by godep.
+
+Please do not edit.
+
+See https://github.com/tools/godep for more information.

+ 2 - 0
Godeps/_workspace/.gitignore

@@ -0,0 +1,2 @@
+/pkg
+/bin

+ 23 - 0
Godeps/_workspace/src/github.com/coreos/go-etcd/etcd/add_child.go

@@ -0,0 +1,23 @@
+package etcd
+
+// Add a new directory with a random etcd-generated key under the given path.
+func (c *Client) AddChildDir(key string, ttl uint64) (*Response, error) {
+	raw, err := c.post(key, "", ttl)
+
+	if err != nil {
+		return nil, err
+	}
+
+	return raw.Unmarshal()
+}
+
+// Add a new file with a random etcd-generated key under the given path.
+func (c *Client) AddChild(key string, value string, ttl uint64) (*Response, error) {
+	raw, err := c.post(key, value, ttl)
+
+	if err != nil {
+		return nil, err
+	}
+
+	return raw.Unmarshal()
+}

+ 73 - 0
Godeps/_workspace/src/github.com/coreos/go-etcd/etcd/add_child_test.go

@@ -0,0 +1,73 @@
+package etcd
+
+import "testing"
+
+func TestAddChild(t *testing.T) {
+	c := NewClient(nil)
+	defer func() {
+		c.Delete("fooDir", true)
+		c.Delete("nonexistentDir", true)
+	}()
+
+	c.CreateDir("fooDir", 5)
+
+	_, err := c.AddChild("fooDir", "v0", 5)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	_, err = c.AddChild("fooDir", "v1", 5)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	resp, err := c.Get("fooDir", true, false)
+	// The child with v0 should proceed the child with v1 because it's added
+	// earlier, so it should have a lower key.
+	if !(len(resp.Node.Nodes) == 2 && (resp.Node.Nodes[0].Value == "v0" && resp.Node.Nodes[1].Value == "v1")) {
+		t.Fatalf("AddChild 1 failed.  There should be two chlidren whose values are v0 and v1, respectively."+
+			"  The response was: %#v", resp)
+	}
+
+	// Creating a child under a nonexistent directory should succeed.
+	// The directory should be created.
+	resp, err = c.AddChild("nonexistentDir", "foo", 5)
+	if err != nil {
+		t.Fatal(err)
+	}
+}
+
+func TestAddChildDir(t *testing.T) {
+	c := NewClient(nil)
+	defer func() {
+		c.Delete("fooDir", true)
+		c.Delete("nonexistentDir", true)
+	}()
+
+	c.CreateDir("fooDir", 5)
+
+	_, err := c.AddChildDir("fooDir", 5)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	_, err = c.AddChildDir("fooDir", 5)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	resp, err := c.Get("fooDir", true, false)
+	// The child with v0 should proceed the child with v1 because it's added
+	// earlier, so it should have a lower key.
+	if !(len(resp.Node.Nodes) == 2 && (len(resp.Node.Nodes[0].Nodes) == 0 && len(resp.Node.Nodes[1].Nodes) == 0)) {
+		t.Fatalf("AddChildDir 1 failed.  There should be two chlidren whose values are v0 and v1, respectively."+
+			"  The response was: %#v", resp)
+	}
+
+	// Creating a child under a nonexistent directory should succeed.
+	// The directory should be created.
+	resp, err = c.AddChildDir("nonexistentDir", 5)
+	if err != nil {
+		t.Fatal(err)
+	}
+}

+ 435 - 0
Godeps/_workspace/src/github.com/coreos/go-etcd/etcd/client.go

@@ -0,0 +1,435 @@
+package etcd
+
+import (
+	"crypto/tls"
+	"crypto/x509"
+	"encoding/json"
+	"errors"
+	"io"
+	"io/ioutil"
+	"net"
+	"net/http"
+	"net/url"
+	"os"
+	"path"
+	"time"
+)
+
+// See SetConsistency for how to use these constants.
+const (
+	// Using strings rather than iota because the consistency level
+	// could be persisted to disk, so it'd be better to use
+	// human-readable values.
+	STRONG_CONSISTENCY = "STRONG"
+	WEAK_CONSISTENCY   = "WEAK"
+)
+
+const (
+	defaultBufferSize = 10
+)
+
+type Config struct {
+	CertFile    string        `json:"certFile"`
+	KeyFile     string        `json:"keyFile"`
+	CaCertFile  []string      `json:"caCertFiles"`
+	DialTimeout time.Duration `json:"timeout"`
+	Consistency string        `json:"consistency"`
+}
+
+type Client struct {
+	config      Config   `json:"config"`
+	cluster     *Cluster `json:"cluster"`
+	httpClient  *http.Client
+	persistence io.Writer
+	cURLch      chan string
+	// CheckRetry can be used to control the policy for failed requests
+	// and modify the cluster if needed.
+	// The client calls it before sending requests again, and
+	// stops retrying if CheckRetry returns some error. The cases that
+	// this function needs to handle include no response and unexpected
+	// http status code of response.
+	// If CheckRetry is nil, client will call the default one
+	// `DefaultCheckRetry`.
+	// Argument cluster is the etcd.Cluster object that these requests have been made on.
+	// Argument numReqs is the number of http.Requests that have been made so far.
+	// Argument lastResp is the http.Responses from the last request.
+	// Argument err is the reason of the failure.
+	CheckRetry func(cluster *Cluster, numReqs int,
+		lastResp http.Response, err error) error
+}
+
+// NewClient create a basic client that is configured to be used
+// with the given machine list.
+func NewClient(machines []string) *Client {
+	config := Config{
+		// default timeout is one second
+		DialTimeout: time.Second,
+		// default consistency level is STRONG
+		Consistency: STRONG_CONSISTENCY,
+	}
+
+	client := &Client{
+		cluster: NewCluster(machines),
+		config:  config,
+	}
+
+	client.initHTTPClient()
+	client.saveConfig()
+
+	return client
+}
+
+// NewTLSClient create a basic client with TLS configuration
+func NewTLSClient(machines []string, cert, key, caCert string) (*Client, error) {
+	// overwrite the default machine to use https
+	if len(machines) == 0 {
+		machines = []string{"https://127.0.0.1:4001"}
+	}
+
+	config := Config{
+		// default timeout is one second
+		DialTimeout: time.Second,
+		// default consistency level is STRONG
+		Consistency: STRONG_CONSISTENCY,
+		CertFile:    cert,
+		KeyFile:     key,
+		CaCertFile:  make([]string, 0),
+	}
+
+	client := &Client{
+		cluster: NewCluster(machines),
+		config:  config,
+	}
+
+	err := client.initHTTPSClient(cert, key)
+	if err != nil {
+		return nil, err
+	}
+
+	err = client.AddRootCA(caCert)
+
+	client.saveConfig()
+
+	return client, nil
+}
+
+// NewClientFromFile creates a client from a given file path.
+// The given file is expected to use the JSON format.
+func NewClientFromFile(fpath string) (*Client, error) {
+	fi, err := os.Open(fpath)
+	if err != nil {
+		return nil, err
+	}
+
+	defer func() {
+		if err := fi.Close(); err != nil {
+			panic(err)
+		}
+	}()
+
+	return NewClientFromReader(fi)
+}
+
+// NewClientFromReader creates a Client configured from a given reader.
+// The configuration is expected to use the JSON format.
+func NewClientFromReader(reader io.Reader) (*Client, error) {
+	c := new(Client)
+
+	b, err := ioutil.ReadAll(reader)
+	if err != nil {
+		return nil, err
+	}
+
+	err = json.Unmarshal(b, c)
+	if err != nil {
+		return nil, err
+	}
+	if c.config.CertFile == "" {
+		c.initHTTPClient()
+	} else {
+		err = c.initHTTPSClient(c.config.CertFile, c.config.KeyFile)
+	}
+
+	if err != nil {
+		return nil, err
+	}
+
+	for _, caCert := range c.config.CaCertFile {
+		if err := c.AddRootCA(caCert); err != nil {
+			return nil, err
+		}
+	}
+
+	return c, nil
+}
+
+// Override the Client's HTTP Transport object
+func (c *Client) SetTransport(tr *http.Transport) {
+	c.httpClient.Transport = tr
+}
+
+// initHTTPClient initializes a HTTP client for etcd client
+func (c *Client) initHTTPClient() {
+	tr := &http.Transport{
+		Dial: c.dial,
+		TLSClientConfig: &tls.Config{
+			InsecureSkipVerify: true,
+		},
+	}
+	c.httpClient = &http.Client{Transport: tr}
+}
+
+// initHTTPClient initializes a HTTPS client for etcd client
+func (c *Client) initHTTPSClient(cert, key string) error {
+	if cert == "" || key == "" {
+		return errors.New("Require both cert and key path")
+	}
+
+	tlsCert, err := tls.LoadX509KeyPair(cert, key)
+	if err != nil {
+		return err
+	}
+
+	tlsConfig := &tls.Config{
+		Certificates:       []tls.Certificate{tlsCert},
+		InsecureSkipVerify: true,
+	}
+
+	tr := &http.Transport{
+		TLSClientConfig: tlsConfig,
+		Dial:            c.dial,
+	}
+
+	c.httpClient = &http.Client{Transport: tr}
+	return nil
+}
+
+// SetPersistence sets a writer to which the config will be
+// written every time it's changed.
+func (c *Client) SetPersistence(writer io.Writer) {
+	c.persistence = writer
+}
+
+// SetConsistency changes the consistency level of the client.
+//
+// When consistency is set to STRONG_CONSISTENCY, all requests,
+// including GET, are sent to the leader.  This means that, assuming
+// the absence of leader failures, GET requests are guaranteed to see
+// the changes made by previous requests.
+//
+// When consistency is set to WEAK_CONSISTENCY, other requests
+// are still sent to the leader, but GET requests are sent to a
+// random server from the server pool.  This reduces the read
+// load on the leader, but it's not guaranteed that the GET requests
+// will see changes made by previous requests (they might have not
+// yet been committed on non-leader servers).
+func (c *Client) SetConsistency(consistency string) error {
+	if !(consistency == STRONG_CONSISTENCY || consistency == WEAK_CONSISTENCY) {
+		return errors.New("The argument must be either STRONG_CONSISTENCY or WEAK_CONSISTENCY.")
+	}
+	c.config.Consistency = consistency
+	return nil
+}
+
+// Sets the DialTimeout value
+func (c *Client) SetDialTimeout(d time.Duration) {
+	c.config.DialTimeout = d
+}
+
+// AddRootCA adds a root CA cert for the etcd client
+func (c *Client) AddRootCA(caCert string) error {
+	if c.httpClient == nil {
+		return errors.New("Client has not been initialized yet!")
+	}
+
+	certBytes, err := ioutil.ReadFile(caCert)
+	if err != nil {
+		return err
+	}
+
+	tr, ok := c.httpClient.Transport.(*http.Transport)
+
+	if !ok {
+		panic("AddRootCA(): Transport type assert should not fail")
+	}
+
+	if tr.TLSClientConfig.RootCAs == nil {
+		caCertPool := x509.NewCertPool()
+		ok = caCertPool.AppendCertsFromPEM(certBytes)
+		if ok {
+			tr.TLSClientConfig.RootCAs = caCertPool
+		}
+		tr.TLSClientConfig.InsecureSkipVerify = false
+	} else {
+		ok = tr.TLSClientConfig.RootCAs.AppendCertsFromPEM(certBytes)
+	}
+
+	if !ok {
+		err = errors.New("Unable to load caCert")
+	}
+
+	c.config.CaCertFile = append(c.config.CaCertFile, caCert)
+	c.saveConfig()
+
+	return err
+}
+
+// SetCluster updates cluster information using the given machine list.
+func (c *Client) SetCluster(machines []string) bool {
+	success := c.internalSyncCluster(machines)
+	return success
+}
+
+func (c *Client) GetCluster() []string {
+	return c.cluster.Machines
+}
+
+// SyncCluster updates the cluster information using the internal machine list.
+func (c *Client) SyncCluster() bool {
+	return c.internalSyncCluster(c.cluster.Machines)
+}
+
+// internalSyncCluster syncs cluster information using the given machine list.
+func (c *Client) internalSyncCluster(machines []string) bool {
+	for _, machine := range machines {
+		httpPath := c.createHttpPath(machine, path.Join(version, "machines"))
+		resp, err := c.httpClient.Get(httpPath)
+		if err != nil {
+			// try another machine in the cluster
+			continue
+		} else {
+			b, err := ioutil.ReadAll(resp.Body)
+			resp.Body.Close()
+			if err != nil {
+				// try another machine in the cluster
+				continue
+			}
+
+			// update Machines List
+			c.cluster.updateFromStr(string(b))
+
+			// update leader
+			// the first one in the machine list is the leader
+			c.cluster.switchLeader(0)
+
+			logger.Debug("sync.machines ", c.cluster.Machines)
+			c.saveConfig()
+			return true
+		}
+	}
+	return false
+}
+
+// createHttpPath creates a complete HTTP URL.
+// serverName should contain both the host name and a port number, if any.
+func (c *Client) createHttpPath(serverName string, _path string) string {
+	u, err := url.Parse(serverName)
+	if err != nil {
+		panic(err)
+	}
+
+	u.Path = path.Join(u.Path, _path)
+
+	if u.Scheme == "" {
+		u.Scheme = "http"
+	}
+	return u.String()
+}
+
+// dial attempts to open a TCP connection to the provided address, explicitly
+// enabling keep-alives with a one-second interval.
+func (c *Client) dial(network, addr string) (net.Conn, error) {
+	conn, err := net.DialTimeout(network, addr, c.config.DialTimeout)
+	if err != nil {
+		return nil, err
+	}
+
+	tcpConn, ok := conn.(*net.TCPConn)
+	if !ok {
+		return nil, errors.New("Failed type-assertion of net.Conn as *net.TCPConn")
+	}
+
+	// Keep TCP alive to check whether or not the remote machine is down
+	if err = tcpConn.SetKeepAlive(true); err != nil {
+		return nil, err
+	}
+
+	if err = tcpConn.SetKeepAlivePeriod(time.Second); err != nil {
+		return nil, err
+	}
+
+	return tcpConn, nil
+}
+
+func (c *Client) OpenCURL() {
+	c.cURLch = make(chan string, defaultBufferSize)
+}
+
+func (c *Client) CloseCURL() {
+	c.cURLch = nil
+}
+
+func (c *Client) sendCURL(command string) {
+	go func() {
+		select {
+		case c.cURLch <- command:
+		default:
+		}
+	}()
+}
+
+func (c *Client) RecvCURL() string {
+	return <-c.cURLch
+}
+
+// saveConfig saves the current config using c.persistence.
+func (c *Client) saveConfig() error {
+	if c.persistence != nil {
+		b, err := json.Marshal(c)
+		if err != nil {
+			return err
+		}
+
+		_, err = c.persistence.Write(b)
+		if err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+// MarshalJSON implements the Marshaller interface
+// as defined by the standard JSON package.
+func (c *Client) MarshalJSON() ([]byte, error) {
+	b, err := json.Marshal(struct {
+		Config  Config   `json:"config"`
+		Cluster *Cluster `json:"cluster"`
+	}{
+		Config:  c.config,
+		Cluster: c.cluster,
+	})
+
+	if err != nil {
+		return nil, err
+	}
+
+	return b, nil
+}
+
+// UnmarshalJSON implements the Unmarshaller interface
+// as defined by the standard JSON package.
+func (c *Client) UnmarshalJSON(b []byte) error {
+	temp := struct {
+		Config  Config   `json:"config"`
+		Cluster *Cluster `json:"cluster"`
+	}{}
+	err := json.Unmarshal(b, &temp)
+	if err != nil {
+		return err
+	}
+
+	c.cluster = temp.Cluster
+	c.config = temp.Config
+	return nil
+}

+ 96 - 0
Godeps/_workspace/src/github.com/coreos/go-etcd/etcd/client_test.go

@@ -0,0 +1,96 @@
+package etcd
+
+import (
+	"encoding/json"
+	"fmt"
+	"net"
+	"net/url"
+	"os"
+	"testing"
+)
+
+// To pass this test, we need to create a cluster of 3 machines
+// The server should be listening on 127.0.0.1:4001, 4002, 4003
+func TestSync(t *testing.T) {
+	fmt.Println("Make sure there are three nodes at 0.0.0.0:4001-4003")
+
+	// Explicit trailing slash to ensure this doesn't reproduce:
+	// https://github.com/coreos/go-etcd/issues/82
+	c := NewClient([]string{"http://127.0.0.1:4001/"})
+
+	success := c.SyncCluster()
+	if !success {
+		t.Fatal("cannot sync machines")
+	}
+
+	for _, m := range c.GetCluster() {
+		u, err := url.Parse(m)
+		if err != nil {
+			t.Fatal(err)
+		}
+		if u.Scheme != "http" {
+			t.Fatal("scheme must be http")
+		}
+
+		host, _, err := net.SplitHostPort(u.Host)
+		if err != nil {
+			t.Fatal(err)
+		}
+		if host != "127.0.0.1" {
+			t.Fatal("Host must be 127.0.0.1")
+		}
+	}
+
+	badMachines := []string{"abc", "edef"}
+
+	success = c.SetCluster(badMachines)
+
+	if success {
+		t.Fatal("should not sync on bad machines")
+	}
+
+	goodMachines := []string{"127.0.0.1:4002"}
+
+	success = c.SetCluster(goodMachines)
+
+	if !success {
+		t.Fatal("cannot sync machines")
+	} else {
+		fmt.Println(c.cluster.Machines)
+	}
+
+}
+
+func TestPersistence(t *testing.T) {
+	c := NewClient(nil)
+	c.SyncCluster()
+
+	fo, err := os.Create("config.json")
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer func() {
+		if err := fo.Close(); err != nil {
+			panic(err)
+		}
+	}()
+
+	c.SetPersistence(fo)
+	err = c.saveConfig()
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	c2, err := NewClientFromFile("config.json")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	// Verify that the two clients have the same config
+	b1, _ := json.Marshal(c)
+	b2, _ := json.Marshal(c2)
+
+	if string(b1) != string(b2) {
+		t.Fatalf("The two configs should be equal!")
+	}
+}

+ 51 - 0
Godeps/_workspace/src/github.com/coreos/go-etcd/etcd/cluster.go

@@ -0,0 +1,51 @@
+package etcd
+
+import (
+	"net/url"
+	"strings"
+)
+
+type Cluster struct {
+	Leader   string   `json:"leader"`
+	Machines []string `json:"machines"`
+}
+
+func NewCluster(machines []string) *Cluster {
+	// if an empty slice was sent in then just assume HTTP 4001 on localhost
+	if len(machines) == 0 {
+		machines = []string{"http://127.0.0.1:4001"}
+	}
+
+	// default leader and machines
+	return &Cluster{
+		Leader:   machines[0],
+		Machines: machines,
+	}
+}
+
+// switchLeader switch the current leader to machines[num]
+func (cl *Cluster) switchLeader(num int) {
+	logger.Debugf("switch.leader[from %v to %v]",
+		cl.Leader, cl.Machines[num])
+
+	cl.Leader = cl.Machines[num]
+}
+
+func (cl *Cluster) updateFromStr(machines string) {
+	cl.Machines = strings.Split(machines, ", ")
+}
+
+func (cl *Cluster) updateLeader(leader string) {
+	logger.Debugf("update.leader[%s,%s]", cl.Leader, leader)
+	cl.Leader = leader
+}
+
+func (cl *Cluster) updateLeaderFromURL(u *url.URL) {
+	var leader string
+	if u.Scheme == "" {
+		leader = "http://" + u.Host
+	} else {
+		leader = u.Scheme + "://" + u.Host
+	}
+	cl.updateLeader(leader)
+}

+ 34 - 0
Godeps/_workspace/src/github.com/coreos/go-etcd/etcd/compare_and_delete.go

@@ -0,0 +1,34 @@
+package etcd
+
+import "fmt"
+
+func (c *Client) CompareAndDelete(key string, prevValue string, prevIndex uint64) (*Response, error) {
+	raw, err := c.RawCompareAndDelete(key, prevValue, prevIndex)
+	if err != nil {
+		return nil, err
+	}
+
+	return raw.Unmarshal()
+}
+
+func (c *Client) RawCompareAndDelete(key string, prevValue string, prevIndex uint64) (*RawResponse, error) {
+	if prevValue == "" && prevIndex == 0 {
+		return nil, fmt.Errorf("You must give either prevValue or prevIndex.")
+	}
+
+	options := Options{}
+	if prevValue != "" {
+		options["prevValue"] = prevValue
+	}
+	if prevIndex != 0 {
+		options["prevIndex"] = prevIndex
+	}
+
+	raw, err := c.delete(key, options)
+
+	if err != nil {
+		return nil, err
+	}
+
+	return raw, err
+}

+ 46 - 0
Godeps/_workspace/src/github.com/coreos/go-etcd/etcd/compare_and_delete_test.go

@@ -0,0 +1,46 @@
+package etcd
+
+import (
+	"testing"
+)
+
+func TestCompareAndDelete(t *testing.T) {
+	c := NewClient(nil)
+	defer func() {
+		c.Delete("foo", true)
+	}()
+
+	c.Set("foo", "bar", 5)
+
+	// This should succeed an correct prevValue
+	resp, err := c.CompareAndDelete("foo", "bar", 0)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if !(resp.PrevNode.Value == "bar" && resp.PrevNode.Key == "/foo" && resp.PrevNode.TTL == 5) {
+		t.Fatalf("CompareAndDelete 1 prevNode failed: %#v", resp)
+	}
+
+	resp, _ = c.Set("foo", "bar", 5)
+	// This should fail because it gives an incorrect prevValue
+	_, err = c.CompareAndDelete("foo", "xxx", 0)
+	if err == nil {
+		t.Fatalf("CompareAndDelete 2 should have failed.  The response is: %#v", resp)
+	}
+
+	// This should succeed because it gives an correct prevIndex
+	resp, err = c.CompareAndDelete("foo", "", resp.Node.ModifiedIndex)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if !(resp.PrevNode.Value == "bar" && resp.PrevNode.Key == "/foo" && resp.PrevNode.TTL == 5) {
+		t.Fatalf("CompareAndSwap 3 prevNode failed: %#v", resp)
+	}
+
+	c.Set("foo", "bar", 5)
+	// This should fail because it gives an incorrect prevIndex
+	resp, err = c.CompareAndDelete("foo", "", 29817514)
+	if err == nil {
+		t.Fatalf("CompareAndDelete 4 should have failed.  The response is: %#v", resp)
+	}
+}

+ 36 - 0
Godeps/_workspace/src/github.com/coreos/go-etcd/etcd/compare_and_swap.go

@@ -0,0 +1,36 @@
+package etcd
+
+import "fmt"
+
+func (c *Client) CompareAndSwap(key string, value string, ttl uint64,
+	prevValue string, prevIndex uint64) (*Response, error) {
+	raw, err := c.RawCompareAndSwap(key, value, ttl, prevValue, prevIndex)
+	if err != nil {
+		return nil, err
+	}
+
+	return raw.Unmarshal()
+}
+
+func (c *Client) RawCompareAndSwap(key string, value string, ttl uint64,
+	prevValue string, prevIndex uint64) (*RawResponse, error) {
+	if prevValue == "" && prevIndex == 0 {
+		return nil, fmt.Errorf("You must give either prevValue or prevIndex.")
+	}
+
+	options := Options{}
+	if prevValue != "" {
+		options["prevValue"] = prevValue
+	}
+	if prevIndex != 0 {
+		options["prevIndex"] = prevIndex
+	}
+
+	raw, err := c.put(key, value, ttl, options)
+
+	if err != nil {
+		return nil, err
+	}
+
+	return raw, err
+}

+ 57 - 0
Godeps/_workspace/src/github.com/coreos/go-etcd/etcd/compare_and_swap_test.go

@@ -0,0 +1,57 @@
+package etcd
+
+import (
+	"testing"
+)
+
+func TestCompareAndSwap(t *testing.T) {
+	c := NewClient(nil)
+	defer func() {
+		c.Delete("foo", true)
+	}()
+
+	c.Set("foo", "bar", 5)
+
+	// This should succeed
+	resp, err := c.CompareAndSwap("foo", "bar2", 5, "bar", 0)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if !(resp.Node.Value == "bar2" && resp.Node.Key == "/foo" && resp.Node.TTL == 5) {
+		t.Fatalf("CompareAndSwap 1 failed: %#v", resp)
+	}
+
+	if !(resp.PrevNode.Value == "bar" && resp.PrevNode.Key == "/foo" && resp.PrevNode.TTL == 5) {
+		t.Fatalf("CompareAndSwap 1 prevNode failed: %#v", resp)
+	}
+
+	// This should fail because it gives an incorrect prevValue
+	resp, err = c.CompareAndSwap("foo", "bar3", 5, "xxx", 0)
+	if err == nil {
+		t.Fatalf("CompareAndSwap 2 should have failed.  The response is: %#v", resp)
+	}
+
+	resp, err = c.Set("foo", "bar", 5)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	// This should succeed
+	resp, err = c.CompareAndSwap("foo", "bar2", 5, "", resp.Node.ModifiedIndex)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if !(resp.Node.Value == "bar2" && resp.Node.Key == "/foo" && resp.Node.TTL == 5) {
+		t.Fatalf("CompareAndSwap 3 failed: %#v", resp)
+	}
+
+	if !(resp.PrevNode.Value == "bar" && resp.PrevNode.Key == "/foo" && resp.PrevNode.TTL == 5) {
+		t.Fatalf("CompareAndSwap 3 prevNode failed: %#v", resp)
+	}
+
+	// This should fail because it gives an incorrect prevIndex
+	resp, err = c.CompareAndSwap("foo", "bar3", 5, "", 29817514)
+	if err == nil {
+		t.Fatalf("CompareAndSwap 4 should have failed.  The response is: %#v", resp)
+	}
+}

+ 55 - 0
Godeps/_workspace/src/github.com/coreos/go-etcd/etcd/debug.go

@@ -0,0 +1,55 @@
+package etcd
+
+import (
+	"fmt"
+	"io/ioutil"
+	"log"
+	"strings"
+)
+
+var logger *etcdLogger
+
+func SetLogger(l *log.Logger) {
+	logger = &etcdLogger{l}
+}
+
+func GetLogger() *log.Logger {
+	return logger.log
+}
+
+type etcdLogger struct {
+	log *log.Logger
+}
+
+func (p *etcdLogger) Debug(args ...interface{}) {
+	msg := "DEBUG: " + fmt.Sprint(args...)
+	p.log.Println(msg)
+}
+
+func (p *etcdLogger) Debugf(f string, args ...interface{}) {
+	msg := "DEBUG: " + fmt.Sprintf(f, args...)
+	// Append newline if necessary
+	if !strings.HasSuffix(msg, "\n") {
+		msg = msg + "\n"
+	}
+	p.log.Print(msg)
+}
+
+func (p *etcdLogger) Warning(args ...interface{}) {
+	msg := "WARNING: " + fmt.Sprint(args...)
+	p.log.Println(msg)
+}
+
+func (p *etcdLogger) Warningf(f string, args ...interface{}) {
+	msg := "WARNING: " + fmt.Sprintf(f, args...)
+	// Append newline if necessary
+	if !strings.HasSuffix(msg, "\n") {
+		msg = msg + "\n"
+	}
+	p.log.Print(msg)
+}
+
+func init() {
+	// Default logger uses the go default log.
+	SetLogger(log.New(ioutil.Discard, "go-etcd", log.LstdFlags))
+}

+ 28 - 0
Godeps/_workspace/src/github.com/coreos/go-etcd/etcd/debug_test.go

@@ -0,0 +1,28 @@
+package etcd
+
+import (
+	"testing"
+)
+
+type Foo struct{}
+type Bar struct {
+	one string
+	two int
+}
+
+// Tests that logs don't panic with arbitrary interfaces
+func TestDebug(t *testing.T) {
+	f := &Foo{}
+	b := &Bar{"asfd", 3}
+	for _, test := range []interface{}{
+		1234,
+		"asdf",
+		f,
+		b,
+	} {
+		logger.Debug(test)
+		logger.Debugf("something, %s", test)
+		logger.Warning(test)
+		logger.Warningf("something, %s", test)
+	}
+}

+ 40 - 0
Godeps/_workspace/src/github.com/coreos/go-etcd/etcd/delete.go

@@ -0,0 +1,40 @@
+package etcd
+
+// Delete deletes the given key.
+//
+// When recursive set to false, if the key points to a
+// directory the method will fail.
+//
+// When recursive set to true, if the key points to a file,
+// the file will be deleted; if the key points to a directory,
+// then everything under the directory (including all child directories)
+// will be deleted.
+func (c *Client) Delete(key string, recursive bool) (*Response, error) {
+	raw, err := c.RawDelete(key, recursive, false)
+
+	if err != nil {
+		return nil, err
+	}
+
+	return raw.Unmarshal()
+}
+
+// DeleteDir deletes an empty directory or a key value pair
+func (c *Client) DeleteDir(key string) (*Response, error) {
+	raw, err := c.RawDelete(key, false, true)
+
+	if err != nil {
+		return nil, err
+	}
+
+	return raw.Unmarshal()
+}
+
+func (c *Client) RawDelete(key string, recursive bool, dir bool) (*RawResponse, error) {
+	ops := Options{
+		"recursive": recursive,
+		"dir":       dir,
+	}
+
+	return c.delete(key, ops)
+}

+ 81 - 0
Godeps/_workspace/src/github.com/coreos/go-etcd/etcd/delete_test.go

@@ -0,0 +1,81 @@
+package etcd
+
+import (
+	"testing"
+)
+
+func TestDelete(t *testing.T) {
+	c := NewClient(nil)
+	defer func() {
+		c.Delete("foo", true)
+	}()
+
+	c.Set("foo", "bar", 5)
+	resp, err := c.Delete("foo", false)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if !(resp.Node.Value == "") {
+		t.Fatalf("Delete failed with %s", resp.Node.Value)
+	}
+
+	if !(resp.PrevNode.Value == "bar") {
+		t.Fatalf("Delete PrevNode failed with %s", resp.Node.Value)
+	}
+
+	resp, err = c.Delete("foo", false)
+	if err == nil {
+		t.Fatalf("Delete should have failed because the key foo did not exist.  "+
+			"The response was: %v", resp)
+	}
+}
+
+func TestDeleteAll(t *testing.T) {
+	c := NewClient(nil)
+	defer func() {
+		c.Delete("foo", true)
+		c.Delete("fooDir", true)
+	}()
+
+	c.SetDir("foo", 5)
+	// test delete an empty dir
+	resp, err := c.DeleteDir("foo")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if !(resp.Node.Value == "") {
+		t.Fatalf("DeleteAll 1 failed: %#v", resp)
+	}
+
+	if !(resp.PrevNode.Dir == true && resp.PrevNode.Value == "") {
+		t.Fatalf("DeleteAll 1 PrevNode failed: %#v", resp)
+	}
+
+	c.CreateDir("fooDir", 5)
+	c.Set("fooDir/foo", "bar", 5)
+	_, err = c.DeleteDir("fooDir")
+	if err == nil {
+		t.Fatal("should not able to delete a non-empty dir with deletedir")
+	}
+
+	resp, err = c.Delete("fooDir", true)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if !(resp.Node.Value == "") {
+		t.Fatalf("DeleteAll 2 failed: %#v", resp)
+	}
+
+	if !(resp.PrevNode.Dir == true && resp.PrevNode.Value == "") {
+		t.Fatalf("DeleteAll 2 PrevNode failed: %#v", resp)
+	}
+
+	resp, err = c.Delete("foo", true)
+	if err == nil {
+		t.Fatalf("DeleteAll should have failed because the key foo did not exist.  "+
+			"The response was: %v", resp)
+	}
+}

+ 48 - 0
Godeps/_workspace/src/github.com/coreos/go-etcd/etcd/error.go

@@ -0,0 +1,48 @@
+package etcd
+
+import (
+	"encoding/json"
+	"fmt"
+)
+
+const (
+	ErrCodeEtcdNotReachable = 501
+)
+
+var (
+	errorMap = map[int]string{
+		ErrCodeEtcdNotReachable: "All the given peers are not reachable",
+	}
+)
+
+type EtcdError struct {
+	ErrorCode int    `json:"errorCode"`
+	Message   string `json:"message"`
+	Cause     string `json:"cause,omitempty"`
+	Index     uint64 `json:"index"`
+}
+
+func (e EtcdError) Error() string {
+	return fmt.Sprintf("%v: %v (%v) [%v]", e.ErrorCode, e.Message, e.Cause, e.Index)
+}
+
+func newError(errorCode int, cause string, index uint64) *EtcdError {
+	return &EtcdError{
+		ErrorCode: errorCode,
+		Message:   errorMap[errorCode],
+		Cause:     cause,
+		Index:     index,
+	}
+}
+
+func handleError(b []byte) error {
+	etcdErr := new(EtcdError)
+
+	err := json.Unmarshal(b, etcdErr)
+	if err != nil {
+		logger.Warningf("cannot unmarshal etcd error: %v", err)
+		return err
+	}
+
+	return etcdErr
+}

+ 27 - 0
Godeps/_workspace/src/github.com/coreos/go-etcd/etcd/get.go

@@ -0,0 +1,27 @@
+package etcd
+
+// Get gets the file or directory associated with the given key.
+// If the key points to a directory, files and directories under
+// it will be returned in sorted or unsorted order, depending on
+// the sort flag.
+// If recursive is set to false, contents under child directories
+// will not be returned.
+// If recursive is set to true, all the contents will be returned.
+func (c *Client) Get(key string, sort, recursive bool) (*Response, error) {
+	raw, err := c.RawGet(key, sort, recursive)
+
+	if err != nil {
+		return nil, err
+	}
+
+	return raw.Unmarshal()
+}
+
+func (c *Client) RawGet(key string, sort, recursive bool) (*RawResponse, error) {
+	ops := Options{
+		"recursive": recursive,
+		"sorted":    sort,
+	}
+
+	return c.get(key, ops)
+}

+ 131 - 0
Godeps/_workspace/src/github.com/coreos/go-etcd/etcd/get_test.go

@@ -0,0 +1,131 @@
+package etcd
+
+import (
+	"reflect"
+	"testing"
+)
+
+// cleanNode scrubs Expiration, ModifiedIndex and CreatedIndex of a node.
+func cleanNode(n *Node) {
+	n.Expiration = nil
+	n.ModifiedIndex = 0
+	n.CreatedIndex = 0
+}
+
+// cleanResult scrubs a result object two levels deep of Expiration,
+// ModifiedIndex and CreatedIndex.
+func cleanResult(result *Response) {
+	//  TODO(philips): make this recursive.
+	cleanNode(result.Node)
+	for i, _ := range result.Node.Nodes {
+		cleanNode(result.Node.Nodes[i])
+		for j, _ := range result.Node.Nodes[i].Nodes {
+			cleanNode(result.Node.Nodes[i].Nodes[j])
+		}
+	}
+}
+
+func TestGet(t *testing.T) {
+	c := NewClient(nil)
+	defer func() {
+		c.Delete("foo", true)
+	}()
+
+	c.Set("foo", "bar", 5)
+
+	result, err := c.Get("foo", false, false)
+
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if result.Node.Key != "/foo" || result.Node.Value != "bar" {
+		t.Fatalf("Get failed with %s %s %v", result.Node.Key, result.Node.Value, result.Node.TTL)
+	}
+
+	result, err = c.Get("goo", false, false)
+	if err == nil {
+		t.Fatalf("should not be able to get non-exist key")
+	}
+}
+
+func TestGetAll(t *testing.T) {
+	c := NewClient(nil)
+	defer func() {
+		c.Delete("fooDir", true)
+	}()
+
+	c.CreateDir("fooDir", 5)
+	c.Set("fooDir/k0", "v0", 5)
+	c.Set("fooDir/k1", "v1", 5)
+
+	// Return kv-pairs in sorted order
+	result, err := c.Get("fooDir", true, false)
+
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	expected := Nodes{
+		&Node{
+			Key:   "/fooDir/k0",
+			Value: "v0",
+			TTL:   5,
+		},
+		&Node{
+			Key:   "/fooDir/k1",
+			Value: "v1",
+			TTL:   5,
+		},
+	}
+
+	cleanResult(result)
+
+	if !reflect.DeepEqual(result.Node.Nodes, expected) {
+		t.Fatalf("(actual) %v != (expected) %v", result.Node.Nodes, expected)
+	}
+
+	// Test the `recursive` option
+	c.CreateDir("fooDir/childDir", 5)
+	c.Set("fooDir/childDir/k2", "v2", 5)
+
+	// Return kv-pairs in sorted order
+	result, err = c.Get("fooDir", true, true)
+
+	cleanResult(result)
+
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	expected = Nodes{
+		&Node{
+			Key: "/fooDir/childDir",
+			Dir: true,
+			Nodes: Nodes{
+				&Node{
+					Key:   "/fooDir/childDir/k2",
+					Value: "v2",
+					TTL:   5,
+				},
+			},
+			TTL: 5,
+		},
+		&Node{
+			Key:   "/fooDir/k0",
+			Value: "v0",
+			TTL:   5,
+		},
+		&Node{
+			Key:   "/fooDir/k1",
+			Value: "v1",
+			TTL:   5,
+		},
+	}
+
+	cleanResult(result)
+
+	if !reflect.DeepEqual(result.Node.Nodes, expected) {
+		t.Fatalf("(actual) %v != (expected) %v", result.Node.Nodes, expected)
+	}
+}

+ 72 - 0
Godeps/_workspace/src/github.com/coreos/go-etcd/etcd/options.go

@@ -0,0 +1,72 @@
+package etcd
+
+import (
+	"fmt"
+	"net/url"
+	"reflect"
+)
+
+type Options map[string]interface{}
+
+// An internally-used data structure that represents a mapping
+// between valid options and their kinds
+type validOptions map[string]reflect.Kind
+
+// Valid options for GET, PUT, POST, DELETE
+// Using CAPITALIZED_UNDERSCORE to emphasize that these
+// values are meant to be used as constants.
+var (
+	VALID_GET_OPTIONS = validOptions{
+		"recursive":  reflect.Bool,
+		"consistent": reflect.Bool,
+		"sorted":     reflect.Bool,
+		"wait":       reflect.Bool,
+		"waitIndex":  reflect.Uint64,
+	}
+
+	VALID_PUT_OPTIONS = validOptions{
+		"prevValue": reflect.String,
+		"prevIndex": reflect.Uint64,
+		"prevExist": reflect.Bool,
+		"dir":       reflect.Bool,
+	}
+
+	VALID_POST_OPTIONS = validOptions{}
+
+	VALID_DELETE_OPTIONS = validOptions{
+		"recursive": reflect.Bool,
+		"dir":       reflect.Bool,
+		"prevValue": reflect.String,
+		"prevIndex": reflect.Uint64,
+	}
+)
+
+// Convert options to a string of HTML parameters
+func (ops Options) toParameters(validOps validOptions) (string, error) {
+	p := "?"
+	values := url.Values{}
+
+	if ops == nil {
+		return "", nil
+	}
+
+	for k, v := range ops {
+		// Check if the given option is valid (that it exists)
+		kind := validOps[k]
+		if kind == reflect.Invalid {
+			return "", fmt.Errorf("Invalid option: %v", k)
+		}
+
+		// Check if the given option is of the valid type
+		t := reflect.TypeOf(v)
+		if kind != t.Kind() {
+			return "", fmt.Errorf("Option %s should be of %v kind, not of %v kind.",
+				k, kind, t.Kind())
+		}
+
+		values.Set(k, fmt.Sprintf("%v", v))
+	}
+
+	p += values.Encode()
+	return p, nil
+}

+ 377 - 0
Godeps/_workspace/src/github.com/coreos/go-etcd/etcd/requests.go

@@ -0,0 +1,377 @@
+package etcd
+
+import (
+	"errors"
+	"fmt"
+	"io/ioutil"
+	"math/rand"
+	"net/http"
+	"net/url"
+	"path"
+	"strings"
+	"sync"
+	"time"
+)
+
+// Errors introduced by handling requests
+var (
+	ErrRequestCancelled = errors.New("sending request is cancelled")
+)
+
+type RawRequest struct {
+	Method       string
+	RelativePath string
+	Values       url.Values
+	Cancel       <-chan bool
+}
+
+// NewRawRequest returns a new RawRequest
+func NewRawRequest(method, relativePath string, values url.Values, cancel <-chan bool) *RawRequest {
+	return &RawRequest{
+		Method:       method,
+		RelativePath: relativePath,
+		Values:       values,
+		Cancel:       cancel,
+	}
+}
+
+// getCancelable issues a cancelable GET request
+func (c *Client) getCancelable(key string, options Options,
+	cancel <-chan bool) (*RawResponse, error) {
+	logger.Debugf("get %s [%s]", key, c.cluster.Leader)
+	p := keyToPath(key)
+
+	// If consistency level is set to STRONG, append
+	// the `consistent` query string.
+	if c.config.Consistency == STRONG_CONSISTENCY {
+		options["consistent"] = true
+	}
+
+	str, err := options.toParameters(VALID_GET_OPTIONS)
+	if err != nil {
+		return nil, err
+	}
+	p += str
+
+	req := NewRawRequest("GET", p, nil, cancel)
+	resp, err := c.SendRequest(req)
+
+	if err != nil {
+		return nil, err
+	}
+
+	return resp, nil
+}
+
+// get issues a GET request
+func (c *Client) get(key string, options Options) (*RawResponse, error) {
+	return c.getCancelable(key, options, nil)
+}
+
+// put issues a PUT request
+func (c *Client) put(key string, value string, ttl uint64,
+	options Options) (*RawResponse, error) {
+
+	logger.Debugf("put %s, %s, ttl: %d, [%s]", key, value, ttl, c.cluster.Leader)
+	p := keyToPath(key)
+
+	str, err := options.toParameters(VALID_PUT_OPTIONS)
+	if err != nil {
+		return nil, err
+	}
+	p += str
+
+	req := NewRawRequest("PUT", p, buildValues(value, ttl), nil)
+	resp, err := c.SendRequest(req)
+
+	if err != nil {
+		return nil, err
+	}
+
+	return resp, nil
+}
+
+// post issues a POST request
+func (c *Client) post(key string, value string, ttl uint64) (*RawResponse, error) {
+	logger.Debugf("post %s, %s, ttl: %d, [%s]", key, value, ttl, c.cluster.Leader)
+	p := keyToPath(key)
+
+	req := NewRawRequest("POST", p, buildValues(value, ttl), nil)
+	resp, err := c.SendRequest(req)
+
+	if err != nil {
+		return nil, err
+	}
+
+	return resp, nil
+}
+
+// delete issues a DELETE request
+func (c *Client) delete(key string, options Options) (*RawResponse, error) {
+	logger.Debugf("delete %s [%s]", key, c.cluster.Leader)
+	p := keyToPath(key)
+
+	str, err := options.toParameters(VALID_DELETE_OPTIONS)
+	if err != nil {
+		return nil, err
+	}
+	p += str
+
+	req := NewRawRequest("DELETE", p, nil, nil)
+	resp, err := c.SendRequest(req)
+
+	if err != nil {
+		return nil, err
+	}
+
+	return resp, nil
+}
+
+// SendRequest sends a HTTP request and returns a Response as defined by etcd
+func (c *Client) SendRequest(rr *RawRequest) (*RawResponse, error) {
+
+	var req *http.Request
+	var resp *http.Response
+	var httpPath string
+	var err error
+	var respBody []byte
+
+	var numReqs = 1
+
+	checkRetry := c.CheckRetry
+	if checkRetry == nil {
+		checkRetry = DefaultCheckRetry
+	}
+
+	cancelled := make(chan bool, 1)
+	reqLock := new(sync.Mutex)
+
+	if rr.Cancel != nil {
+		cancelRoutine := make(chan bool)
+		defer close(cancelRoutine)
+
+		go func() {
+			select {
+			case <-rr.Cancel:
+				cancelled <- true
+				logger.Debug("send.request is cancelled")
+			case <-cancelRoutine:
+				return
+			}
+
+			// Repeat canceling request until this thread is stopped
+			// because we have no idea about whether it succeeds.
+			for {
+				reqLock.Lock()
+				c.httpClient.Transport.(*http.Transport).CancelRequest(req)
+				reqLock.Unlock()
+
+				select {
+				case <-time.After(100 * time.Millisecond):
+				case <-cancelRoutine:
+					return
+				}
+			}
+		}()
+	}
+
+	// If we connect to a follower and consistency is required, retry until
+	// we connect to a leader
+	sleep := 25 * time.Millisecond
+	maxSleep := time.Second
+	for attempt := 0; ; attempt++ {
+		if attempt > 0 {
+			select {
+			case <-cancelled:
+				return nil, ErrRequestCancelled
+			case <-time.After(sleep):
+				sleep = sleep * 2
+				if sleep > maxSleep {
+					sleep = maxSleep
+				}
+			}
+		}
+
+		logger.Debug("Connecting to etcd: attempt", attempt+1, "for", rr.RelativePath)
+
+		if rr.Method == "GET" && c.config.Consistency == WEAK_CONSISTENCY {
+			// If it's a GET and consistency level is set to WEAK,
+			// then use a random machine.
+			httpPath = c.getHttpPath(true, rr.RelativePath)
+		} else {
+			// Else use the leader.
+			httpPath = c.getHttpPath(false, rr.RelativePath)
+		}
+
+		// Return a cURL command if curlChan is set
+		if c.cURLch != nil {
+			command := fmt.Sprintf("curl -X %s %s", rr.Method, httpPath)
+			for key, value := range rr.Values {
+				command += fmt.Sprintf(" -d %s=%s", key, value[0])
+			}
+			c.sendCURL(command)
+		}
+
+		logger.Debug("send.request.to ", httpPath, " | method ", rr.Method)
+
+		reqLock.Lock()
+		if rr.Values == nil {
+			if req, err = http.NewRequest(rr.Method, httpPath, nil); err != nil {
+				return nil, err
+			}
+		} else {
+			body := strings.NewReader(rr.Values.Encode())
+			if req, err = http.NewRequest(rr.Method, httpPath, body); err != nil {
+				return nil, err
+			}
+
+			req.Header.Set("Content-Type",
+				"application/x-www-form-urlencoded; param=value")
+		}
+		reqLock.Unlock()
+
+		resp, err = c.httpClient.Do(req)
+		defer func() {
+			if resp != nil {
+				resp.Body.Close()
+			}
+		}()
+
+		// If the request was cancelled, return ErrRequestCancelled directly
+		select {
+		case <-cancelled:
+			return nil, ErrRequestCancelled
+		default:
+		}
+
+		numReqs++
+
+		// network error, change a machine!
+		if err != nil {
+			logger.Debug("network error:", err.Error())
+			lastResp := http.Response{}
+			if checkErr := checkRetry(c.cluster, numReqs, lastResp, err); checkErr != nil {
+				return nil, checkErr
+			}
+
+			c.cluster.switchLeader(attempt % len(c.cluster.Machines))
+			continue
+		}
+
+		// if there is no error, it should receive response
+		logger.Debug("recv.response.from", httpPath)
+
+		if validHttpStatusCode[resp.StatusCode] {
+			// try to read byte code and break the loop
+			respBody, err = ioutil.ReadAll(resp.Body)
+			if err == nil {
+				logger.Debug("recv.success.", httpPath)
+				break
+			}
+			// ReadAll error may be caused due to cancel request
+			select {
+			case <-cancelled:
+				return nil, ErrRequestCancelled
+			default:
+			}
+		}
+
+		// if resp is TemporaryRedirect, set the new leader and retry
+		if resp.StatusCode == http.StatusTemporaryRedirect {
+			u, err := resp.Location()
+
+			if err != nil {
+				logger.Warning(err)
+			} else {
+				// Update cluster leader based on redirect location
+				// because it should point to the leader address
+				c.cluster.updateLeaderFromURL(u)
+				logger.Debug("recv.response.relocate", u.String())
+			}
+			resp.Body.Close()
+			continue
+		}
+
+		if checkErr := checkRetry(c.cluster, numReqs, *resp,
+			errors.New("Unexpected HTTP status code")); checkErr != nil {
+			return nil, checkErr
+		}
+		resp.Body.Close()
+	}
+
+	r := &RawResponse{
+		StatusCode: resp.StatusCode,
+		Body:       respBody,
+		Header:     resp.Header,
+	}
+
+	return r, nil
+}
+
+// DefaultCheckRetry defines the retrying behaviour for bad HTTP requests
+// If we have retried 2 * machine number, stop retrying.
+// If status code is InternalServerError, sleep for 200ms.
+func DefaultCheckRetry(cluster *Cluster, numReqs int, lastResp http.Response,
+	err error) error {
+
+	if numReqs >= 2*len(cluster.Machines) {
+		return newError(ErrCodeEtcdNotReachable,
+			"Tried to connect to each peer twice and failed", 0)
+	}
+
+	code := lastResp.StatusCode
+	if code == http.StatusInternalServerError {
+		time.Sleep(time.Millisecond * 200)
+
+	}
+
+	logger.Warning("bad response status code", code)
+	return nil
+}
+
+func (c *Client) getHttpPath(random bool, s ...string) string {
+	var machine string
+	if random {
+		machine = c.cluster.Machines[rand.Intn(len(c.cluster.Machines))]
+	} else {
+		machine = c.cluster.Leader
+	}
+
+	fullPath := machine + "/" + version
+	for _, seg := range s {
+		fullPath = fullPath + "/" + seg
+	}
+
+	return fullPath
+}
+
+// buildValues builds a url.Values map according to the given value and ttl
+func buildValues(value string, ttl uint64) url.Values {
+	v := url.Values{}
+
+	if value != "" {
+		v.Set("value", value)
+	}
+
+	if ttl > 0 {
+		v.Set("ttl", fmt.Sprintf("%v", ttl))
+	}
+
+	return v
+}
+
+// convert key string to http path exclude version
+// for example: key[foo] -> path[keys/foo]
+// key[/] -> path[keys/]
+func keyToPath(key string) string {
+	p := path.Join("keys", key)
+
+	// corner case: if key is "/" or "//" ect
+	// path join will clear the tailing "/"
+	// we need to add it back
+	if p == "keys" {
+		p = "keys/"
+	}
+
+	return p
+}

+ 89 - 0
Godeps/_workspace/src/github.com/coreos/go-etcd/etcd/response.go

@@ -0,0 +1,89 @@
+package etcd
+
+import (
+	"encoding/json"
+	"net/http"
+	"strconv"
+	"time"
+)
+
+const (
+	rawResponse = iota
+	normalResponse
+)
+
+type responseType int
+
+type RawResponse struct {
+	StatusCode int
+	Body       []byte
+	Header     http.Header
+}
+
+var (
+	validHttpStatusCode = map[int]bool{
+		http.StatusCreated:            true,
+		http.StatusOK:                 true,
+		http.StatusBadRequest:         true,
+		http.StatusNotFound:           true,
+		http.StatusPreconditionFailed: true,
+		http.StatusForbidden:          true,
+	}
+)
+
+// Unmarshal parses RawResponse and stores the result in Response
+func (rr *RawResponse) Unmarshal() (*Response, error) {
+	if rr.StatusCode != http.StatusOK && rr.StatusCode != http.StatusCreated {
+		return nil, handleError(rr.Body)
+	}
+
+	resp := new(Response)
+
+	err := json.Unmarshal(rr.Body, resp)
+
+	if err != nil {
+		return nil, err
+	}
+
+	// attach index and term to response
+	resp.EtcdIndex, _ = strconv.ParseUint(rr.Header.Get("X-Etcd-Index"), 10, 64)
+	resp.RaftIndex, _ = strconv.ParseUint(rr.Header.Get("X-Raft-Index"), 10, 64)
+	resp.RaftTerm, _ = strconv.ParseUint(rr.Header.Get("X-Raft-Term"), 10, 64)
+
+	return resp, nil
+}
+
+type Response struct {
+	Action    string `json:"action"`
+	Node      *Node  `json:"node"`
+	PrevNode  *Node  `json:"prevNode,omitempty"`
+	EtcdIndex uint64 `json:"etcdIndex"`
+	RaftIndex uint64 `json:"raftIndex"`
+	RaftTerm  uint64 `json:"raftTerm"`
+}
+
+type Node struct {
+	Key           string     `json:"key, omitempty"`
+	Value         string     `json:"value,omitempty"`
+	Dir           bool       `json:"dir,omitempty"`
+	Expiration    *time.Time `json:"expiration,omitempty"`
+	TTL           int64      `json:"ttl,omitempty"`
+	Nodes         Nodes      `json:"nodes,omitempty"`
+	ModifiedIndex uint64     `json:"modifiedIndex,omitempty"`
+	CreatedIndex  uint64     `json:"createdIndex,omitempty"`
+}
+
+type Nodes []*Node
+
+// interfaces for sorting
+func (ns Nodes) Len() int {
+	return len(ns)
+}
+
+func (ns Nodes) Less(i, j int) bool {
+	return ns[i].Key < ns[j].Key
+}
+
+func (ns Nodes) Swap(i, j int) {
+	ns[i], ns[j] = ns[j], ns[i]
+}

+ 42 - 0
Godeps/_workspace/src/github.com/coreos/go-etcd/etcd/set_curl_chan_test.go

@@ -0,0 +1,42 @@
+package etcd
+
+import (
+	"fmt"
+	"testing"
+)
+
+func TestSetCurlChan(t *testing.T) {
+	c := NewClient(nil)
+	c.OpenCURL()
+
+	defer func() {
+		c.Delete("foo", true)
+	}()
+
+	_, err := c.Set("foo", "bar", 5)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	expected := fmt.Sprintf("curl -X PUT %s/v2/keys/foo -d value=bar -d ttl=5",
+		c.cluster.Leader)
+	actual := c.RecvCURL()
+	if expected != actual {
+		t.Fatalf(`Command "%s" is not equal to expected value "%s"`,
+			actual, expected)
+	}
+
+	c.SetConsistency(STRONG_CONSISTENCY)
+	_, err = c.Get("foo", false, false)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	expected = fmt.Sprintf("curl -X GET %s/v2/keys/foo?consistent=true&recursive=false&sorted=false",
+		c.cluster.Leader)
+	actual = c.RecvCURL()
+	if expected != actual {
+		t.Fatalf(`Command "%s" is not equal to expected value "%s"`,
+			actual, expected)
+	}
+}

+ 137 - 0
Godeps/_workspace/src/github.com/coreos/go-etcd/etcd/set_update_create.go

@@ -0,0 +1,137 @@
+package etcd
+
+// Set sets the given key to the given value.
+// It will create a new key value pair or replace the old one.
+// It will not replace a existing directory.
+func (c *Client) Set(key string, value string, ttl uint64) (*Response, error) {
+	raw, err := c.RawSet(key, value, ttl)
+
+	if err != nil {
+		return nil, err
+	}
+
+	return raw.Unmarshal()
+}
+
+// Set sets the given key to a directory.
+// It will create a new directory or replace the old key value pair by a directory.
+// It will not replace a existing directory.
+func (c *Client) SetDir(key string, ttl uint64) (*Response, error) {
+	raw, err := c.RawSetDir(key, ttl)
+
+	if err != nil {
+		return nil, err
+	}
+
+	return raw.Unmarshal()
+}
+
+// CreateDir creates a directory. It succeeds only if
+// the given key does not yet exist.
+func (c *Client) CreateDir(key string, ttl uint64) (*Response, error) {
+	raw, err := c.RawCreateDir(key, ttl)
+
+	if err != nil {
+		return nil, err
+	}
+
+	return raw.Unmarshal()
+}
+
+// UpdateDir updates the given directory. It succeeds only if the
+// given key already exists.
+func (c *Client) UpdateDir(key string, ttl uint64) (*Response, error) {
+	raw, err := c.RawUpdateDir(key, ttl)
+
+	if err != nil {
+		return nil, err
+	}
+
+	return raw.Unmarshal()
+}
+
+// Create creates a file with the given value under the given key.  It succeeds
+// only if the given key does not yet exist.
+func (c *Client) Create(key string, value string, ttl uint64) (*Response, error) {
+	raw, err := c.RawCreate(key, value, ttl)
+
+	if err != nil {
+		return nil, err
+	}
+
+	return raw.Unmarshal()
+}
+
+// CreateInOrder creates a file with a key that's guaranteed to be higher than other
+// keys in the given directory. It is useful for creating queues.
+func (c *Client) CreateInOrder(dir string, value string, ttl uint64) (*Response, error) {
+	raw, err := c.RawCreateInOrder(dir, value, ttl)
+
+	if err != nil {
+		return nil, err
+	}
+
+	return raw.Unmarshal()
+}
+
+// Update updates the given key to the given value.  It succeeds only if the
+// given key already exists.
+func (c *Client) Update(key string, value string, ttl uint64) (*Response, error) {
+	raw, err := c.RawUpdate(key, value, ttl)
+
+	if err != nil {
+		return nil, err
+	}
+
+	return raw.Unmarshal()
+}
+
+func (c *Client) RawUpdateDir(key string, ttl uint64) (*RawResponse, error) {
+	ops := Options{
+		"prevExist": true,
+		"dir":       true,
+	}
+
+	return c.put(key, "", ttl, ops)
+}
+
+func (c *Client) RawCreateDir(key string, ttl uint64) (*RawResponse, error) {
+	ops := Options{
+		"prevExist": false,
+		"dir":       true,
+	}
+
+	return c.put(key, "", ttl, ops)
+}
+
+func (c *Client) RawSet(key string, value string, ttl uint64) (*RawResponse, error) {
+	return c.put(key, value, ttl, nil)
+}
+
+func (c *Client) RawSetDir(key string, ttl uint64) (*RawResponse, error) {
+	ops := Options{
+		"dir": true,
+	}
+
+	return c.put(key, "", ttl, ops)
+}
+
+func (c *Client) RawUpdate(key string, value string, ttl uint64) (*RawResponse, error) {
+	ops := Options{
+		"prevExist": true,
+	}
+
+	return c.put(key, value, ttl, ops)
+}
+
+func (c *Client) RawCreate(key string, value string, ttl uint64) (*RawResponse, error) {
+	ops := Options{
+		"prevExist": false,
+	}
+
+	return c.put(key, value, ttl, ops)
+}
+
+func (c *Client) RawCreateInOrder(dir string, value string, ttl uint64) (*RawResponse, error) {
+	return c.post(dir, value, ttl)
+}

+ 241 - 0
Godeps/_workspace/src/github.com/coreos/go-etcd/etcd/set_update_create_test.go

@@ -0,0 +1,241 @@
+package etcd
+
+import (
+	"testing"
+)
+
+func TestSet(t *testing.T) {
+	c := NewClient(nil)
+	defer func() {
+		c.Delete("foo", true)
+	}()
+
+	resp, err := c.Set("foo", "bar", 5)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if resp.Node.Key != "/foo" || resp.Node.Value != "bar" || resp.Node.TTL != 5 {
+		t.Fatalf("Set 1 failed: %#v", resp)
+	}
+	if resp.PrevNode != nil {
+		t.Fatalf("Set 1 PrevNode failed: %#v", resp)
+	}
+
+	resp, err = c.Set("foo", "bar2", 5)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if !(resp.Node.Key == "/foo" && resp.Node.Value == "bar2" && resp.Node.TTL == 5) {
+		t.Fatalf("Set 2 failed: %#v", resp)
+	}
+	if resp.PrevNode.Key != "/foo" || resp.PrevNode.Value != "bar" || resp.Node.TTL != 5 {
+		t.Fatalf("Set 2 PrevNode failed: %#v", resp)
+	}
+}
+
+func TestUpdate(t *testing.T) {
+	c := NewClient(nil)
+	defer func() {
+		c.Delete("foo", true)
+		c.Delete("nonexistent", true)
+	}()
+
+	resp, err := c.Set("foo", "bar", 5)
+
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	// This should succeed.
+	resp, err = c.Update("foo", "wakawaka", 5)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if !(resp.Action == "update" && resp.Node.Key == "/foo" && resp.Node.TTL == 5) {
+		t.Fatalf("Update 1 failed: %#v", resp)
+	}
+	if !(resp.PrevNode.Key == "/foo" && resp.PrevNode.Value == "bar" && resp.Node.TTL == 5) {
+		t.Fatalf("Update 1 prevValue failed: %#v", resp)
+	}
+
+	// This should fail because the key does not exist.
+	resp, err = c.Update("nonexistent", "whatever", 5)
+	if err == nil {
+		t.Fatalf("The key %v did not exist, so the update should have failed."+
+			"The response was: %#v", resp.Node.Key, resp)
+	}
+}
+
+func TestCreate(t *testing.T) {
+	c := NewClient(nil)
+	defer func() {
+		c.Delete("newKey", true)
+	}()
+
+	newKey := "/newKey"
+	newValue := "/newValue"
+
+	// This should succeed
+	resp, err := c.Create(newKey, newValue, 5)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if !(resp.Action == "create" && resp.Node.Key == newKey &&
+		resp.Node.Value == newValue && resp.Node.TTL == 5) {
+		t.Fatalf("Create 1 failed: %#v", resp)
+	}
+	if resp.PrevNode != nil {
+		t.Fatalf("Create 1 PrevNode failed: %#v", resp)
+	}
+
+	// This should fail, because the key is already there
+	resp, err = c.Create(newKey, newValue, 5)
+	if err == nil {
+		t.Fatalf("The key %v did exist, so the creation should have failed."+
+			"The response was: %#v", resp.Node.Key, resp)
+	}
+}
+
+func TestCreateInOrder(t *testing.T) {
+	c := NewClient(nil)
+	dir := "/queue"
+	defer func() {
+		c.DeleteDir(dir)
+	}()
+
+	var firstKey, secondKey string
+
+	resp, err := c.CreateInOrder(dir, "1", 5)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if !(resp.Action == "create" && resp.Node.Value == "1" && resp.Node.TTL == 5) {
+		t.Fatalf("Create 1 failed: %#v", resp)
+	}
+
+	firstKey = resp.Node.Key
+
+	resp, err = c.CreateInOrder(dir, "2", 5)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if !(resp.Action == "create" && resp.Node.Value == "2" && resp.Node.TTL == 5) {
+		t.Fatalf("Create 2 failed: %#v", resp)
+	}
+
+	secondKey = resp.Node.Key
+
+	if firstKey >= secondKey {
+		t.Fatalf("Expected first key to be greater than second key, but %s is not greater than %s",
+			firstKey, secondKey)
+	}
+}
+
+func TestSetDir(t *testing.T) {
+	c := NewClient(nil)
+	defer func() {
+		c.Delete("foo", true)
+		c.Delete("fooDir", true)
+	}()
+
+	resp, err := c.CreateDir("fooDir", 5)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if !(resp.Node.Key == "/fooDir" && resp.Node.Value == "" && resp.Node.TTL == 5) {
+		t.Fatalf("SetDir 1 failed: %#v", resp)
+	}
+	if resp.PrevNode != nil {
+		t.Fatalf("SetDir 1 PrevNode failed: %#v", resp)
+	}
+
+	// This should fail because /fooDir already points to a directory
+	resp, err = c.CreateDir("/fooDir", 5)
+	if err == nil {
+		t.Fatalf("fooDir already points to a directory, so SetDir should have failed."+
+			"The response was: %#v", resp)
+	}
+
+	_, err = c.Set("foo", "bar", 5)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	// This should succeed
+	// It should replace the key
+	resp, err = c.SetDir("foo", 5)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if !(resp.Node.Key == "/foo" && resp.Node.Value == "" && resp.Node.TTL == 5) {
+		t.Fatalf("SetDir 2 failed: %#v", resp)
+	}
+	if !(resp.PrevNode.Key == "/foo" && resp.PrevNode.Value == "bar" && resp.PrevNode.TTL == 5) {
+		t.Fatalf("SetDir 2 failed: %#v", resp)
+	}
+}
+
+func TestUpdateDir(t *testing.T) {
+	c := NewClient(nil)
+	defer func() {
+		c.Delete("fooDir", true)
+	}()
+
+	resp, err := c.CreateDir("fooDir", 5)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	// This should succeed.
+	resp, err = c.UpdateDir("fooDir", 5)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if !(resp.Action == "update" && resp.Node.Key == "/fooDir" &&
+		resp.Node.Value == "" && resp.Node.TTL == 5) {
+		t.Fatalf("UpdateDir 1 failed: %#v", resp)
+	}
+	if !(resp.PrevNode.Key == "/fooDir" && resp.PrevNode.Dir == true && resp.PrevNode.TTL == 5) {
+		t.Fatalf("UpdateDir 1 PrevNode failed: %#v", resp)
+	}
+
+	// This should fail because the key does not exist.
+	resp, err = c.UpdateDir("nonexistentDir", 5)
+	if err == nil {
+		t.Fatalf("The key %v did not exist, so the update should have failed."+
+			"The response was: %#v", resp.Node.Key, resp)
+	}
+}
+
+func TestCreateDir(t *testing.T) {
+	c := NewClient(nil)
+	defer func() {
+		c.Delete("fooDir", true)
+	}()
+
+	// This should succeed
+	resp, err := c.CreateDir("fooDir", 5)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if !(resp.Action == "create" && resp.Node.Key == "/fooDir" &&
+		resp.Node.Value == "" && resp.Node.TTL == 5) {
+		t.Fatalf("CreateDir 1 failed: %#v", resp)
+	}
+	if resp.PrevNode != nil {
+		t.Fatalf("CreateDir 1 PrevNode failed: %#v", resp)
+	}
+
+	// This should fail, because the key is already there
+	resp, err = c.CreateDir("fooDir", 5)
+	if err == nil {
+		t.Fatalf("The key %v did exist, so the creation should have failed."+
+			"The response was: %#v", resp.Node.Key, resp)
+	}
+}

+ 3 - 0
Godeps/_workspace/src/github.com/coreos/go-etcd/etcd/version.go

@@ -0,0 +1,3 @@
+package etcd
+
+const version = "v2"

+ 103 - 0
Godeps/_workspace/src/github.com/coreos/go-etcd/etcd/watch.go

@@ -0,0 +1,103 @@
+package etcd
+
+import (
+	"errors"
+)
+
+// Errors introduced by the Watch command.
+var (
+	ErrWatchStoppedByUser = errors.New("Watch stopped by the user via stop channel")
+)
+
+// If recursive is set to true the watch returns the first change under the given
+// prefix since the given index.
+//
+// If recursive is set to false the watch returns the first change to the given key
+// since the given index.
+//
+// To watch for the latest change, set waitIndex = 0.
+//
+// If a receiver channel is given, it will be a long-term watch. Watch will block at the
+//channel. After someone receives the channel, it will go on to watch that
+// prefix.  If a stop channel is given, the client can close long-term watch using
+// the stop channel.
+func (c *Client) Watch(prefix string, waitIndex uint64, recursive bool,
+	receiver chan *Response, stop chan bool) (*Response, error) {
+	logger.Debugf("watch %s [%s]", prefix, c.cluster.Leader)
+	if receiver == nil {
+		raw, err := c.watchOnce(prefix, waitIndex, recursive, stop)
+
+		if err != nil {
+			return nil, err
+		}
+
+		return raw.Unmarshal()
+	}
+	defer close(receiver)
+
+	for {
+		raw, err := c.watchOnce(prefix, waitIndex, recursive, stop)
+
+		if err != nil {
+			return nil, err
+		}
+
+		resp, err := raw.Unmarshal()
+
+		if err != nil {
+			return nil, err
+		}
+
+		waitIndex = resp.Node.ModifiedIndex + 1
+		receiver <- resp
+	}
+}
+
+func (c *Client) RawWatch(prefix string, waitIndex uint64, recursive bool,
+	receiver chan *RawResponse, stop chan bool) (*RawResponse, error) {
+
+	logger.Debugf("rawWatch %s [%s]", prefix, c.cluster.Leader)
+	if receiver == nil {
+		return c.watchOnce(prefix, waitIndex, recursive, stop)
+	}
+
+	for {
+		raw, err := c.watchOnce(prefix, waitIndex, recursive, stop)
+
+		if err != nil {
+			return nil, err
+		}
+
+		resp, err := raw.Unmarshal()
+
+		if err != nil {
+			return nil, err
+		}
+
+		waitIndex = resp.Node.ModifiedIndex + 1
+		receiver <- raw
+	}
+}
+
+// helper func
+// return when there is change under the given prefix
+func (c *Client) watchOnce(key string, waitIndex uint64, recursive bool, stop chan bool) (*RawResponse, error) {
+
+	options := Options{
+		"wait": true,
+	}
+	if waitIndex > 0 {
+		options["waitIndex"] = waitIndex
+	}
+	if recursive {
+		options["recursive"] = true
+	}
+
+	resp, err := c.getCancelable(key, options, stop)
+
+	if err == ErrRequestCancelled {
+		return nil, ErrWatchStoppedByUser
+	}
+
+	return resp, err
+}

+ 119 - 0
Godeps/_workspace/src/github.com/coreos/go-etcd/etcd/watch_test.go

@@ -0,0 +1,119 @@
+package etcd
+
+import (
+	"fmt"
+	"runtime"
+	"testing"
+	"time"
+)
+
+func TestWatch(t *testing.T) {
+	c := NewClient(nil)
+	defer func() {
+		c.Delete("watch_foo", true)
+	}()
+
+	go setHelper("watch_foo", "bar", c)
+
+	resp, err := c.Watch("watch_foo", 0, false, nil, nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if !(resp.Node.Key == "/watch_foo" && resp.Node.Value == "bar") {
+		t.Fatalf("Watch 1 failed: %#v", resp)
+	}
+
+	go setHelper("watch_foo", "bar", c)
+
+	resp, err = c.Watch("watch_foo", resp.Node.ModifiedIndex+1, false, nil, nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if !(resp.Node.Key == "/watch_foo" && resp.Node.Value == "bar") {
+		t.Fatalf("Watch 2 failed: %#v", resp)
+	}
+
+	routineNum := runtime.NumGoroutine()
+
+	ch := make(chan *Response, 10)
+	stop := make(chan bool, 1)
+
+	go setLoop("watch_foo", "bar", c)
+
+	go receiver(ch, stop)
+
+	_, err = c.Watch("watch_foo", 0, false, ch, stop)
+	if err != ErrWatchStoppedByUser {
+		t.Fatalf("Watch returned a non-user stop error")
+	}
+
+	if newRoutineNum := runtime.NumGoroutine(); newRoutineNum != routineNum {
+		t.Fatalf("Routine numbers differ after watch stop: %v, %v", routineNum, newRoutineNum)
+	}
+}
+
+func TestWatchAll(t *testing.T) {
+	c := NewClient(nil)
+	defer func() {
+		c.Delete("watch_foo", true)
+	}()
+
+	go setHelper("watch_foo/foo", "bar", c)
+
+	resp, err := c.Watch("watch_foo", 0, true, nil, nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if !(resp.Node.Key == "/watch_foo/foo" && resp.Node.Value == "bar") {
+		t.Fatalf("WatchAll 1 failed: %#v", resp)
+	}
+
+	go setHelper("watch_foo/foo", "bar", c)
+
+	resp, err = c.Watch("watch_foo", resp.Node.ModifiedIndex+1, true, nil, nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if !(resp.Node.Key == "/watch_foo/foo" && resp.Node.Value == "bar") {
+		t.Fatalf("WatchAll 2 failed: %#v", resp)
+	}
+
+	ch := make(chan *Response, 10)
+	stop := make(chan bool, 1)
+
+	routineNum := runtime.NumGoroutine()
+
+	go setLoop("watch_foo/foo", "bar", c)
+
+	go receiver(ch, stop)
+
+	_, err = c.Watch("watch_foo", 0, true, ch, stop)
+	if err != ErrWatchStoppedByUser {
+		t.Fatalf("Watch returned a non-user stop error")
+	}
+
+	if newRoutineNum := runtime.NumGoroutine(); newRoutineNum != routineNum {
+		t.Fatalf("Routine numbers differ after watch stop: %v, %v", routineNum, newRoutineNum)
+	}
+}
+
+func setHelper(key, value string, c *Client) {
+	time.Sleep(time.Second)
+	c.Set(key, value, 100)
+}
+
+func setLoop(key, value string, c *Client) {
+	time.Sleep(time.Second)
+	for i := 0; i < 10; i++ {
+		newValue := fmt.Sprintf("%s_%v", value, i)
+		c.Set(key, newValue, 100)
+		time.Sleep(time.Second / 10)
+	}
+}
+
+func receiver(c chan *Response, stop chan bool) {
+	for i := 0; i < 10; i++ {
+		<-c
+	}
+	stop <- true
+}

+ 31 - 0
Godeps/_workspace/src/github.com/coreos/go-systemd/daemon/sdnotify.go

@@ -0,0 +1,31 @@
+// Code forked from Docker project
+package daemon
+
+import (
+	"errors"
+	"net"
+	"os"
+)
+
+var SdNotifyNoSocket = errors.New("No socket")
+
+// SdNotify sends a message to the init daemon. It is common to ignore the error.
+func SdNotify(state string) error {
+	socketAddr := &net.UnixAddr{
+		Name: os.Getenv("NOTIFY_SOCKET"),
+		Net:  "unixgram",
+	}
+
+	if socketAddr.Name == "" {
+		return SdNotifyNoSocket
+	}
+
+	conn, err := net.DialUnix(socketAddr.Net, nil, socketAddr)
+	if err != nil {
+		return err
+	}
+	defer conn.Close()
+
+	_, err = conn.Write([]byte(state))
+	return err
+}

+ 2 - 0
Godeps/_workspace/src/github.com/docker/libcontainer/netlink/MAINTAINERS

@@ -0,0 +1,2 @@
+Michael Crosby <michael@crosbymichael.com> (@crosbymichael)
+Guillaume J. Charmes <guillaume@docker.com> (@creack)

+ 23 - 0
Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink.go

@@ -0,0 +1,23 @@
+// Packet netlink provide access to low level Netlink sockets and messages.
+//
+// Actual implementations are in:
+// netlink_linux.go
+// netlink_darwin.go
+package netlink
+
+import (
+	"errors"
+	"net"
+)
+
+var (
+	ErrWrongSockType = errors.New("Wrong socket type")
+	ErrShortResponse = errors.New("Got short response from netlink")
+)
+
+// A Route is a subnet associated with the interface to reach it.
+type Route struct {
+	*net.IPNet
+	Iface   *net.Interface
+	Default bool
+}

+ 1002 - 0
Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink_linux.go

@@ -0,0 +1,1002 @@
+package netlink
+
+import (
+	"encoding/binary"
+	"fmt"
+	"net"
+	"sync/atomic"
+	"syscall"
+	"unsafe"
+)
+
+const (
+	IFNAMSIZ       = 16
+	DEFAULT_CHANGE = 0xFFFFFFFF
+	IFLA_INFO_KIND = 1
+	IFLA_INFO_DATA = 2
+	VETH_INFO_PEER = 1
+	IFLA_NET_NS_FD = 28
+	SIOC_BRADDBR   = 0x89a0
+	SIOC_BRDELBR   = 0x89a1
+	SIOC_BRADDIF   = 0x89a2
+)
+
+var nextSeqNr uint32
+
+type ifreqHwaddr struct {
+	IfrnName   [IFNAMSIZ]byte
+	IfruHwaddr syscall.RawSockaddr
+}
+
+type ifreqIndex struct {
+	IfrnName  [IFNAMSIZ]byte
+	IfruIndex int32
+}
+
+type ifreqFlags struct {
+	IfrnName  [IFNAMSIZ]byte
+	Ifruflags uint16
+}
+
+func nativeEndian() binary.ByteOrder {
+	var x uint32 = 0x01020304
+	if *(*byte)(unsafe.Pointer(&x)) == 0x01 {
+		return binary.BigEndian
+	}
+	return binary.LittleEndian
+}
+
+func getIpFamily(ip net.IP) int {
+	if len(ip) <= net.IPv4len {
+		return syscall.AF_INET
+	}
+	if ip.To4() != nil {
+		return syscall.AF_INET
+	}
+	return syscall.AF_INET6
+}
+
+type NetlinkRequestData interface {
+	Len() int
+	ToWireFormat() []byte
+}
+
+type IfInfomsg struct {
+	syscall.IfInfomsg
+}
+
+func newIfInfomsg(family int) *IfInfomsg {
+	return &IfInfomsg{
+		IfInfomsg: syscall.IfInfomsg{
+			Family: uint8(family),
+		},
+	}
+}
+
+func newIfInfomsgChild(parent *RtAttr, family int) *IfInfomsg {
+	msg := newIfInfomsg(family)
+	parent.children = append(parent.children, msg)
+	return msg
+}
+
+func (msg *IfInfomsg) ToWireFormat() []byte {
+	native := nativeEndian()
+
+	length := syscall.SizeofIfInfomsg
+	b := make([]byte, length)
+	b[0] = msg.Family
+	b[1] = 0
+	native.PutUint16(b[2:4], msg.Type)
+	native.PutUint32(b[4:8], uint32(msg.Index))
+	native.PutUint32(b[8:12], msg.Flags)
+	native.PutUint32(b[12:16], msg.Change)
+	return b
+}
+
+func (msg *IfInfomsg) Len() int {
+	return syscall.SizeofIfInfomsg
+}
+
+type IfAddrmsg struct {
+	syscall.IfAddrmsg
+}
+
+func newIfAddrmsg(family int) *IfAddrmsg {
+	return &IfAddrmsg{
+		IfAddrmsg: syscall.IfAddrmsg{
+			Family: uint8(family),
+		},
+	}
+}
+
+func (msg *IfAddrmsg) ToWireFormat() []byte {
+	native := nativeEndian()
+
+	length := syscall.SizeofIfAddrmsg
+	b := make([]byte, length)
+	b[0] = msg.Family
+	b[1] = msg.Prefixlen
+	b[2] = msg.Flags
+	b[3] = msg.Scope
+	native.PutUint32(b[4:8], msg.Index)
+	return b
+}
+
+func (msg *IfAddrmsg) Len() int {
+	return syscall.SizeofIfAddrmsg
+}
+
+type RtMsg struct {
+	syscall.RtMsg
+}
+
+func newRtMsg() *RtMsg {
+	return &RtMsg{
+		RtMsg: syscall.RtMsg{
+			Table:    syscall.RT_TABLE_MAIN,
+			Scope:    syscall.RT_SCOPE_UNIVERSE,
+			Protocol: syscall.RTPROT_BOOT,
+			Type:     syscall.RTN_UNICAST,
+		},
+	}
+}
+
+func (msg *RtMsg) ToWireFormat() []byte {
+	native := nativeEndian()
+
+	length := syscall.SizeofRtMsg
+	b := make([]byte, length)
+	b[0] = msg.Family
+	b[1] = msg.Dst_len
+	b[2] = msg.Src_len
+	b[3] = msg.Tos
+	b[4] = msg.Table
+	b[5] = msg.Protocol
+	b[6] = msg.Scope
+	b[7] = msg.Type
+	native.PutUint32(b[8:12], msg.Flags)
+	return b
+}
+
+func (msg *RtMsg) Len() int {
+	return syscall.SizeofRtMsg
+}
+
+func rtaAlignOf(attrlen int) int {
+	return (attrlen + syscall.RTA_ALIGNTO - 1) & ^(syscall.RTA_ALIGNTO - 1)
+}
+
+type RtAttr struct {
+	syscall.RtAttr
+	Data     []byte
+	children []NetlinkRequestData
+}
+
+func newRtAttr(attrType int, data []byte) *RtAttr {
+	return &RtAttr{
+		RtAttr: syscall.RtAttr{
+			Type: uint16(attrType),
+		},
+		children: []NetlinkRequestData{},
+		Data:     data,
+	}
+}
+
+func newRtAttrChild(parent *RtAttr, attrType int, data []byte) *RtAttr {
+	attr := newRtAttr(attrType, data)
+	parent.children = append(parent.children, attr)
+	return attr
+}
+
+func (a *RtAttr) Len() int {
+	l := 0
+	for _, child := range a.children {
+		l += child.Len() + syscall.SizeofRtAttr
+	}
+	if l == 0 {
+		l++
+	}
+	return rtaAlignOf(l + len(a.Data))
+}
+
+func (a *RtAttr) ToWireFormat() []byte {
+	native := nativeEndian()
+
+	length := a.Len()
+	buf := make([]byte, rtaAlignOf(length+syscall.SizeofRtAttr))
+
+	if a.Data != nil {
+		copy(buf[4:], a.Data)
+	} else {
+		next := 4
+		for _, child := range a.children {
+			childBuf := child.ToWireFormat()
+			copy(buf[next:], childBuf)
+			next += rtaAlignOf(len(childBuf))
+		}
+	}
+
+	if l := uint16(rtaAlignOf(length)); l != 0 {
+		native.PutUint16(buf[0:2], l+1)
+	}
+	native.PutUint16(buf[2:4], a.Type)
+
+	return buf
+}
+
+type NetlinkRequest struct {
+	syscall.NlMsghdr
+	Data []NetlinkRequestData
+}
+
+func (rr *NetlinkRequest) ToWireFormat() []byte {
+	native := nativeEndian()
+
+	length := rr.Len
+	dataBytes := make([][]byte, len(rr.Data))
+	for i, data := range rr.Data {
+		dataBytes[i] = data.ToWireFormat()
+		length += uint32(len(dataBytes[i]))
+	}
+	b := make([]byte, length)
+	native.PutUint32(b[0:4], length)
+	native.PutUint16(b[4:6], rr.Type)
+	native.PutUint16(b[6:8], rr.Flags)
+	native.PutUint32(b[8:12], rr.Seq)
+	native.PutUint32(b[12:16], rr.Pid)
+
+	next := 16
+	for _, data := range dataBytes {
+		copy(b[next:], data)
+		next += len(data)
+	}
+	return b
+}
+
+func (rr *NetlinkRequest) AddData(data NetlinkRequestData) {
+	if data != nil {
+		rr.Data = append(rr.Data, data)
+	}
+}
+
+func newNetlinkRequest(proto, flags int) *NetlinkRequest {
+	return &NetlinkRequest{
+		NlMsghdr: syscall.NlMsghdr{
+			Len:   uint32(syscall.NLMSG_HDRLEN),
+			Type:  uint16(proto),
+			Flags: syscall.NLM_F_REQUEST | uint16(flags),
+			Seq:   atomic.AddUint32(&nextSeqNr, 1),
+		},
+	}
+}
+
+type NetlinkSocket struct {
+	fd  int
+	lsa syscall.SockaddrNetlink
+}
+
+func getNetlinkSocket() (*NetlinkSocket, error) {
+	fd, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_RAW, syscall.NETLINK_ROUTE)
+	if err != nil {
+		return nil, err
+	}
+	s := &NetlinkSocket{
+		fd: fd,
+	}
+	s.lsa.Family = syscall.AF_NETLINK
+	if err := syscall.Bind(fd, &s.lsa); err != nil {
+		syscall.Close(fd)
+		return nil, err
+	}
+
+	return s, nil
+}
+
+func (s *NetlinkSocket) Close() {
+	syscall.Close(s.fd)
+}
+
+func (s *NetlinkSocket) Send(request *NetlinkRequest) error {
+	if err := syscall.Sendto(s.fd, request.ToWireFormat(), 0, &s.lsa); err != nil {
+		return err
+	}
+	return nil
+}
+
+func (s *NetlinkSocket) Receive() ([]syscall.NetlinkMessage, error) {
+	rb := make([]byte, syscall.Getpagesize())
+	nr, _, err := syscall.Recvfrom(s.fd, rb, 0)
+	if err != nil {
+		return nil, err
+	}
+	if nr < syscall.NLMSG_HDRLEN {
+		return nil, ErrShortResponse
+	}
+	rb = rb[:nr]
+	return syscall.ParseNetlinkMessage(rb)
+}
+
+func (s *NetlinkSocket) GetPid() (uint32, error) {
+	lsa, err := syscall.Getsockname(s.fd)
+	if err != nil {
+		return 0, err
+	}
+	switch v := lsa.(type) {
+	case *syscall.SockaddrNetlink:
+		return v.Pid, nil
+	}
+	return 0, ErrWrongSockType
+}
+
+func (s *NetlinkSocket) HandleAck(seq uint32) error {
+	native := nativeEndian()
+
+	pid, err := s.GetPid()
+	if err != nil {
+		return err
+	}
+
+done:
+	for {
+		msgs, err := s.Receive()
+		if err != nil {
+			return err
+		}
+		for _, m := range msgs {
+			if m.Header.Seq != seq {
+				return fmt.Errorf("Wrong Seq nr %d, expected %d", m.Header.Seq, seq)
+			}
+			if m.Header.Pid != pid {
+				return fmt.Errorf("Wrong pid %d, expected %d", m.Header.Pid, pid)
+			}
+			if m.Header.Type == syscall.NLMSG_DONE {
+				break done
+			}
+			if m.Header.Type == syscall.NLMSG_ERROR {
+				error := int32(native.Uint32(m.Data[0:4]))
+				if error == 0 {
+					break done
+				}
+				return syscall.Errno(-error)
+			}
+		}
+	}
+
+	return nil
+}
+
+// Add a new route table entry.
+func AddRoute(destination, source, gateway, device string) error {
+	if destination == "" && source == "" && gateway == "" {
+		return fmt.Errorf("one of destination, source or gateway must not be blank")
+	}
+
+	s, err := getNetlinkSocket()
+	if err != nil {
+		return err
+	}
+	defer s.Close()
+
+	wb := newNetlinkRequest(syscall.RTM_NEWROUTE, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
+	msg := newRtMsg()
+	currentFamily := -1
+	var rtAttrs []*RtAttr
+
+	if destination != "" {
+		destIP, destNet, err := net.ParseCIDR(destination)
+		if err != nil {
+			return fmt.Errorf("destination CIDR %s couldn't be parsed", destination)
+		}
+		destFamily := getIpFamily(destIP)
+		currentFamily = destFamily
+		destLen, bits := destNet.Mask.Size()
+		if destLen == 0 && bits == 0 {
+			return fmt.Errorf("destination CIDR %s generated a non-canonical Mask", destination)
+		}
+		msg.Family = uint8(destFamily)
+		msg.Dst_len = uint8(destLen)
+		var destData []byte
+		if destFamily == syscall.AF_INET {
+			destData = destIP.To4()
+		} else {
+			destData = destIP.To16()
+		}
+		rtAttrs = append(rtAttrs, newRtAttr(syscall.RTA_DST, destData))
+	}
+
+	if source != "" {
+		srcIP, srcNet, err := net.ParseCIDR(source)
+		if err != nil {
+			return fmt.Errorf("source CIDR %s couldn't be parsed", source)
+		}
+		srcFamily := getIpFamily(srcIP)
+		if currentFamily != -1 && currentFamily != srcFamily {
+			return fmt.Errorf("source and destination ip were not the same IP family")
+		}
+		currentFamily = srcFamily
+		srcLen, bits := srcNet.Mask.Size()
+		if srcLen == 0 && bits == 0 {
+			return fmt.Errorf("source CIDR %s generated a non-canonical Mask", source)
+		}
+		msg.Family = uint8(srcFamily)
+		msg.Src_len = uint8(srcLen)
+		var srcData []byte
+		if srcFamily == syscall.AF_INET {
+			srcData = srcIP.To4()
+		} else {
+			srcData = srcIP.To16()
+		}
+		rtAttrs = append(rtAttrs, newRtAttr(syscall.RTA_SRC, srcData))
+	}
+
+	if gateway != "" {
+		gwIP := net.ParseIP(gateway)
+		if gwIP == nil {
+			return fmt.Errorf("gateway IP %s couldn't be parsed", gateway)
+		}
+		gwFamily := getIpFamily(gwIP)
+		if currentFamily != -1 && currentFamily != gwFamily {
+			return fmt.Errorf("gateway, source, and destination ip were not the same IP family")
+		}
+		msg.Family = uint8(gwFamily)
+		var gwData []byte
+		if gwFamily == syscall.AF_INET {
+			gwData = gwIP.To4()
+		} else {
+			gwData = gwIP.To16()
+		}
+		rtAttrs = append(rtAttrs, newRtAttr(syscall.RTA_GATEWAY, gwData))
+	}
+
+	wb.AddData(msg)
+	for _, attr := range rtAttrs {
+		wb.AddData(attr)
+	}
+
+	var (
+		native = nativeEndian()
+		b      = make([]byte, 4)
+	)
+	iface, err := net.InterfaceByName(device)
+	if err != nil {
+		return err
+	}
+	native.PutUint32(b, uint32(iface.Index))
+
+	wb.AddData(newRtAttr(syscall.RTA_OIF, b))
+
+	if err := s.Send(wb); err != nil {
+		return err
+	}
+	return s.HandleAck(wb.Seq)
+}
+
+// Add a new default gateway. Identical to:
+// ip route add default via $ip
+func AddDefaultGw(ip, device string) error {
+	return AddRoute("", "", ip, device)
+}
+
+// Bring up a particular network interface
+func NetworkLinkUp(iface *net.Interface) error {
+	s, err := getNetlinkSocket()
+	if err != nil {
+		return err
+	}
+	defer s.Close()
+
+	wb := newNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_ACK)
+
+	msg := newIfInfomsg(syscall.AF_UNSPEC)
+	msg.Change = syscall.IFF_UP
+	msg.Flags = syscall.IFF_UP
+	msg.Index = int32(iface.Index)
+	wb.AddData(msg)
+
+	if err := s.Send(wb); err != nil {
+		return err
+	}
+
+	return s.HandleAck(wb.Seq)
+}
+
+func NetworkLinkDown(iface *net.Interface) error {
+	s, err := getNetlinkSocket()
+	if err != nil {
+		return err
+	}
+	defer s.Close()
+
+	wb := newNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_ACK)
+
+	msg := newIfInfomsg(syscall.AF_UNSPEC)
+	msg.Change = syscall.IFF_UP
+	msg.Flags = 0 & ^syscall.IFF_UP
+	msg.Index = int32(iface.Index)
+	wb.AddData(msg)
+
+	if err := s.Send(wb); err != nil {
+		return err
+	}
+
+	return s.HandleAck(wb.Seq)
+}
+
+func NetworkSetMTU(iface *net.Interface, mtu int) error {
+	s, err := getNetlinkSocket()
+	if err != nil {
+		return err
+	}
+	defer s.Close()
+
+	wb := newNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK)
+
+	msg := newIfInfomsg(syscall.AF_UNSPEC)
+	msg.Type = syscall.RTM_SETLINK
+	msg.Flags = syscall.NLM_F_REQUEST
+	msg.Index = int32(iface.Index)
+	msg.Change = DEFAULT_CHANGE
+	wb.AddData(msg)
+
+	var (
+		b      = make([]byte, 4)
+		native = nativeEndian()
+	)
+	native.PutUint32(b, uint32(mtu))
+
+	data := newRtAttr(syscall.IFLA_MTU, b)
+	wb.AddData(data)
+
+	if err := s.Send(wb); err != nil {
+		return err
+	}
+	return s.HandleAck(wb.Seq)
+}
+
+// same as ip link set $name master $master
+func NetworkSetMaster(iface, master *net.Interface) error {
+	s, err := getNetlinkSocket()
+	if err != nil {
+		return err
+	}
+	defer s.Close()
+
+	wb := newNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK)
+
+	msg := newIfInfomsg(syscall.AF_UNSPEC)
+	msg.Type = syscall.RTM_SETLINK
+	msg.Flags = syscall.NLM_F_REQUEST
+	msg.Index = int32(iface.Index)
+	msg.Change = DEFAULT_CHANGE
+	wb.AddData(msg)
+
+	var (
+		b      = make([]byte, 4)
+		native = nativeEndian()
+	)
+	native.PutUint32(b, uint32(master.Index))
+
+	data := newRtAttr(syscall.IFLA_MASTER, b)
+	wb.AddData(data)
+
+	if err := s.Send(wb); err != nil {
+		return err
+	}
+
+	return s.HandleAck(wb.Seq)
+}
+
+func NetworkSetNsPid(iface *net.Interface, nspid int) error {
+	s, err := getNetlinkSocket()
+	if err != nil {
+		return err
+	}
+	defer s.Close()
+
+	wb := newNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK)
+
+	msg := newIfInfomsg(syscall.AF_UNSPEC)
+	msg.Type = syscall.RTM_SETLINK
+	msg.Flags = syscall.NLM_F_REQUEST
+	msg.Index = int32(iface.Index)
+	msg.Change = DEFAULT_CHANGE
+	wb.AddData(msg)
+
+	var (
+		b      = make([]byte, 4)
+		native = nativeEndian()
+	)
+	native.PutUint32(b, uint32(nspid))
+
+	data := newRtAttr(syscall.IFLA_NET_NS_PID, b)
+	wb.AddData(data)
+
+	if err := s.Send(wb); err != nil {
+		return err
+	}
+
+	return s.HandleAck(wb.Seq)
+}
+
+func NetworkSetNsFd(iface *net.Interface, fd int) error {
+	s, err := getNetlinkSocket()
+	if err != nil {
+		return err
+	}
+	defer s.Close()
+
+	wb := newNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK)
+
+	msg := newIfInfomsg(syscall.AF_UNSPEC)
+	msg.Type = syscall.RTM_SETLINK
+	msg.Flags = syscall.NLM_F_REQUEST
+	msg.Index = int32(iface.Index)
+	msg.Change = DEFAULT_CHANGE
+	wb.AddData(msg)
+
+	var (
+		b      = make([]byte, 4)
+		native = nativeEndian()
+	)
+	native.PutUint32(b, uint32(fd))
+
+	data := newRtAttr(IFLA_NET_NS_FD, b)
+	wb.AddData(data)
+
+	if err := s.Send(wb); err != nil {
+		return err
+	}
+
+	return s.HandleAck(wb.Seq)
+}
+
+// Add an Ip address to an interface. This is identical to:
+// ip addr add $ip/$ipNet dev $iface
+func NetworkLinkAddIp(iface *net.Interface, ip net.IP, ipNet *net.IPNet) error {
+	s, err := getNetlinkSocket()
+	if err != nil {
+		return err
+	}
+	defer s.Close()
+
+	family := getIpFamily(ip)
+
+	wb := newNetlinkRequest(syscall.RTM_NEWADDR, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
+
+	msg := newIfAddrmsg(family)
+	msg.Index = uint32(iface.Index)
+	prefixLen, _ := ipNet.Mask.Size()
+	msg.Prefixlen = uint8(prefixLen)
+	wb.AddData(msg)
+
+	var ipData []byte
+	if family == syscall.AF_INET {
+		ipData = ip.To4()
+	} else {
+		ipData = ip.To16()
+	}
+
+	localData := newRtAttr(syscall.IFA_LOCAL, ipData)
+	wb.AddData(localData)
+
+	addrData := newRtAttr(syscall.IFA_ADDRESS, ipData)
+	wb.AddData(addrData)
+
+	if err := s.Send(wb); err != nil {
+		return err
+	}
+
+	return s.HandleAck(wb.Seq)
+}
+
+func zeroTerminated(s string) []byte {
+	return []byte(s + "\000")
+}
+
+func nonZeroTerminated(s string) []byte {
+	return []byte(s)
+}
+
+// Add a new network link of a specified type. This is identical to
+// running: ip add link $name type $linkType
+func NetworkLinkAdd(name string, linkType string) error {
+	s, err := getNetlinkSocket()
+	if err != nil {
+		return err
+	}
+	defer s.Close()
+
+	wb := newNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
+
+	msg := newIfInfomsg(syscall.AF_UNSPEC)
+	wb.AddData(msg)
+
+	if name != "" {
+		nameData := newRtAttr(syscall.IFLA_IFNAME, zeroTerminated(name))
+		wb.AddData(nameData)
+	}
+
+	kindData := newRtAttr(IFLA_INFO_KIND, nonZeroTerminated(linkType))
+
+	infoData := newRtAttr(syscall.IFLA_LINKINFO, kindData.ToWireFormat())
+	wb.AddData(infoData)
+
+	if err := s.Send(wb); err != nil {
+		return err
+	}
+
+	return s.HandleAck(wb.Seq)
+}
+
+// Returns an array of IPNet for all the currently routed subnets on ipv4
+// This is similar to the first column of "ip route" output
+func NetworkGetRoutes() ([]Route, error) {
+	native := nativeEndian()
+
+	s, err := getNetlinkSocket()
+	if err != nil {
+		return nil, err
+	}
+	defer s.Close()
+
+	wb := newNetlinkRequest(syscall.RTM_GETROUTE, syscall.NLM_F_DUMP)
+
+	msg := newIfInfomsg(syscall.AF_UNSPEC)
+	wb.AddData(msg)
+
+	if err := s.Send(wb); err != nil {
+		return nil, err
+	}
+
+	pid, err := s.GetPid()
+	if err != nil {
+		return nil, err
+	}
+
+	res := make([]Route, 0)
+
+done:
+	for {
+		msgs, err := s.Receive()
+		if err != nil {
+			return nil, err
+		}
+		for _, m := range msgs {
+			if m.Header.Seq != wb.Seq {
+				return nil, fmt.Errorf("Wrong Seq nr %d, expected 1", m.Header.Seq)
+			}
+			if m.Header.Pid != pid {
+				return nil, fmt.Errorf("Wrong pid %d, expected %d", m.Header.Pid, pid)
+			}
+			if m.Header.Type == syscall.NLMSG_DONE {
+				break done
+			}
+			if m.Header.Type == syscall.NLMSG_ERROR {
+				error := int32(native.Uint32(m.Data[0:4]))
+				if error == 0 {
+					break done
+				}
+				return nil, syscall.Errno(-error)
+			}
+			if m.Header.Type != syscall.RTM_NEWROUTE {
+				continue
+			}
+
+			var r Route
+
+			msg := (*RtMsg)(unsafe.Pointer(&m.Data[0:syscall.SizeofRtMsg][0]))
+
+			if msg.Flags&syscall.RTM_F_CLONED != 0 {
+				// Ignore cloned routes
+				continue
+			}
+
+			if msg.Table != syscall.RT_TABLE_MAIN {
+				// Ignore non-main tables
+				continue
+			}
+
+			if msg.Family != syscall.AF_INET {
+				// Ignore non-ipv4 routes
+				continue
+			}
+
+			if msg.Dst_len == 0 {
+				// Default routes
+				r.Default = true
+			}
+
+			attrs, err := syscall.ParseNetlinkRouteAttr(&m)
+			if err != nil {
+				return nil, err
+			}
+			for _, attr := range attrs {
+				switch attr.Attr.Type {
+				case syscall.RTA_DST:
+					ip := attr.Value
+					r.IPNet = &net.IPNet{
+						IP:   ip,
+						Mask: net.CIDRMask(int(msg.Dst_len), 8*len(ip)),
+					}
+				case syscall.RTA_OIF:
+					index := int(native.Uint32(attr.Value[0:4]))
+					r.Iface, _ = net.InterfaceByIndex(index)
+				}
+			}
+			if r.Default || r.IPNet != nil {
+				res = append(res, r)
+			}
+		}
+	}
+
+	return res, nil
+}
+
+func getIfSocket() (fd int, err error) {
+	for _, socket := range []int{
+		syscall.AF_INET,
+		syscall.AF_PACKET,
+		syscall.AF_INET6,
+	} {
+		if fd, err = syscall.Socket(socket, syscall.SOCK_DGRAM, 0); err == nil {
+			break
+		}
+	}
+	if err == nil {
+		return fd, nil
+	}
+	return -1, err
+}
+
+func NetworkChangeName(iface *net.Interface, newName string) error {
+	if len(newName) >= IFNAMSIZ {
+		return fmt.Errorf("Interface name %s too long", newName)
+	}
+
+	fd, err := getIfSocket()
+	if err != nil {
+		return err
+	}
+	defer syscall.Close(fd)
+
+	data := [IFNAMSIZ * 2]byte{}
+	// the "-1"s here are very important for ensuring we get proper null
+	// termination of our new C strings
+	copy(data[:IFNAMSIZ-1], iface.Name)
+	copy(data[IFNAMSIZ:IFNAMSIZ*2-1], newName)
+
+	if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), syscall.SIOCSIFNAME, uintptr(unsafe.Pointer(&data[0]))); errno != 0 {
+		return errno
+	}
+	return nil
+}
+
+func NetworkCreateVethPair(name1, name2 string) error {
+	s, err := getNetlinkSocket()
+	if err != nil {
+		return err
+	}
+	defer s.Close()
+
+	wb := newNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
+
+	msg := newIfInfomsg(syscall.AF_UNSPEC)
+	wb.AddData(msg)
+
+	nameData := newRtAttr(syscall.IFLA_IFNAME, zeroTerminated(name1))
+	wb.AddData(nameData)
+
+	nest1 := newRtAttr(syscall.IFLA_LINKINFO, nil)
+	newRtAttrChild(nest1, IFLA_INFO_KIND, zeroTerminated("veth"))
+	nest2 := newRtAttrChild(nest1, IFLA_INFO_DATA, nil)
+	nest3 := newRtAttrChild(nest2, VETH_INFO_PEER, nil)
+
+	newIfInfomsgChild(nest3, syscall.AF_UNSPEC)
+	newRtAttrChild(nest3, syscall.IFLA_IFNAME, zeroTerminated(name2))
+
+	wb.AddData(nest1)
+
+	if err := s.Send(wb); err != nil {
+		return err
+	}
+	return s.HandleAck(wb.Seq)
+}
+
+// Create the actual bridge device.  This is more backward-compatible than
+// netlink.NetworkLinkAdd and works on RHEL 6.
+func CreateBridge(name string, setMacAddr bool) error {
+	if len(name) >= IFNAMSIZ {
+		return fmt.Errorf("Interface name %s too long", name)
+	}
+
+	s, err := getIfSocket()
+	if err != nil {
+		return err
+	}
+	defer syscall.Close(s)
+
+	nameBytePtr, err := syscall.BytePtrFromString(name)
+	if err != nil {
+		return err
+	}
+	if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, uintptr(s), SIOC_BRADDBR, uintptr(unsafe.Pointer(nameBytePtr))); err != 0 {
+		return err
+	}
+	if setMacAddr {
+		return setBridgeMacAddress(s, name)
+	}
+	return nil
+}
+
+// Delete the actual bridge device.
+func DeleteBridge(name string) error {
+	s, err := getIfSocket()
+	if err != nil {
+		return err
+	}
+	defer syscall.Close(s)
+
+	nameBytePtr, err := syscall.BytePtrFromString(name)
+	if err != nil {
+		return err
+	}
+
+	var ifr ifreqFlags
+	copy(ifr.IfrnName[:len(ifr.IfrnName)-1], []byte(name))
+	if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, uintptr(s),
+		syscall.SIOCSIFFLAGS, uintptr(unsafe.Pointer(&ifr))); err != 0 {
+		return err
+	}
+
+	if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, uintptr(s),
+		SIOC_BRDELBR, uintptr(unsafe.Pointer(nameBytePtr))); err != 0 {
+		return err
+	}
+	return nil
+}
+
+// Add a slave to abridge device.  This is more backward-compatible than
+// netlink.NetworkSetMaster and works on RHEL 6.
+func AddToBridge(iface, master *net.Interface) error {
+	if len(master.Name) >= IFNAMSIZ {
+		return fmt.Errorf("Interface name %s too long", master.Name)
+	}
+
+	s, err := getIfSocket()
+	if err != nil {
+		return err
+	}
+	defer syscall.Close(s)
+
+	ifr := ifreqIndex{}
+	copy(ifr.IfrnName[:len(ifr.IfrnName)-1], master.Name)
+	ifr.IfruIndex = int32(iface.Index)
+
+	if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, uintptr(s), SIOC_BRADDIF, uintptr(unsafe.Pointer(&ifr))); err != 0 {
+		return err
+	}
+
+	return nil
+}
+
+func setBridgeMacAddress(s int, name string) error {
+	if len(name) >= IFNAMSIZ {
+		return fmt.Errorf("Interface name %s too long", name)
+	}
+
+	ifr := ifreqHwaddr{}
+	ifr.IfruHwaddr.Family = syscall.ARPHRD_ETHER
+	copy(ifr.IfrnName[:len(ifr.IfrnName)-1], name)
+
+	for i := 0; i < 6; i++ {
+		ifr.IfruHwaddr.Data[i] = randIfrDataByte()
+	}
+
+	ifr.IfruHwaddr.Data[0] &^= 0x1 // clear multicast bit
+	ifr.IfruHwaddr.Data[0] |= 0x2  // set local assignment bit (IEEE802)
+
+	if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, uintptr(s), syscall.SIOCSIFHWADDR, uintptr(unsafe.Pointer(&ifr))); err != 0 {
+		return err
+	}
+	return nil
+}

+ 9 - 0
Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink_linux_arm.go

@@ -0,0 +1,9 @@
+package netlink
+
+import (
+	"math/rand"
+)
+
+func randIfrDataByte() uint8 {
+	return uint8(rand.Intn(255))
+}

+ 11 - 0
Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink_linux_notarm.go

@@ -0,0 +1,11 @@
+// +build !arm
+
+package netlink
+
+import (
+	"math/rand"
+)
+
+func randIfrDataByte() int8 {
+	return int8(rand.Intn(255))
+}

+ 55 - 0
Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink_linux_test.go

@@ -0,0 +1,55 @@
+package netlink
+
+import (
+	"net"
+	"testing"
+)
+
+func TestCreateBridgeWithMac(t *testing.T) {
+	if testing.Short() {
+		return
+	}
+
+	name := "testbridge"
+
+	if err := CreateBridge(name, true); err != nil {
+		t.Fatal(err)
+	}
+
+	if _, err := net.InterfaceByName(name); err != nil {
+		t.Fatal(err)
+	}
+
+	// cleanup and tests
+
+	if err := DeleteBridge(name); err != nil {
+		t.Fatal(err)
+	}
+
+	if _, err := net.InterfaceByName(name); err == nil {
+		t.Fatal("expected error getting interface because bridge was deleted")
+	}
+}
+
+func TestCreateVethPair(t *testing.T) {
+	if testing.Short() {
+		return
+	}
+
+	var (
+		name1 = "veth1"
+		name2 = "veth2"
+	)
+
+	if err := NetworkCreateVethPair(name1, name2); err != nil {
+		t.Fatal(err)
+	}
+
+	if _, err := net.InterfaceByName(name1); err != nil {
+		t.Fatal(err)
+	}
+
+	if _, err := net.InterfaceByName(name2); err != nil {
+		t.Fatal(err)
+	}
+}

+ 76 - 0
Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink_unsupported.go

@@ -0,0 +1,76 @@
+// +build !linux
+
+package netlink
+
+import (
+	"errors"
+	"net"
+)
+
+var (
+	ErrNotImplemented = errors.New("not implemented")
+)
+
+func NetworkGetRoutes() ([]Route, error) {
+	return nil, ErrNotImplemented
+}
+
+func NetworkLinkAdd(name string, linkType string) error {
+	return ErrNotImplemented
+}
+
+func NetworkLinkUp(iface *net.Interface) error {
+	return ErrNotImplemented
+}
+
+func NetworkLinkAddIp(iface *net.Interface, ip net.IP, ipNet *net.IPNet) error {
+	return ErrNotImplemented
+}
+
+func AddRoute(destination, source, gateway, device string) error {
+	return ErrNotImplemented
+}
+
+func AddDefaultGw(ip, device string) error {
+	return ErrNotImplemented
+}
+
+func NetworkSetMTU(iface *net.Interface, mtu int) error {
+	return ErrNotImplemented
+}
+
+func NetworkCreateVethPair(name1, name2 string) error {
+	return ErrNotImplemented
+}
+
+func NetworkChangeName(iface *net.Interface, newName string) error {
+	return ErrNotImplemented
+}
+
+func NetworkSetNsFd(iface *net.Interface, fd int) error {
+	return ErrNotImplemented
+}
+
+func NetworkSetNsPid(iface *net.Interface, nspid int) error {
+	return ErrNotImplemented
+}
+
+func NetworkSetMaster(iface, master *net.Interface) error {
+	return ErrNotImplemented
+}
+
+func NetworkLinkDown(iface *net.Interface) error {
+	return ErrNotImplemented
+}
+
+func CreateBridge(name string, setMacAddr bool) error {
+	return ErrNotImplemented
+}
+
+func DeleteBridge(name string) error {
+	return ErrNotImplemented
+}
+
+func AddToBridge(iface, master *net.Interface) error {
+	return ErrNotImplemented
+}

+ 191 - 0
Godeps/_workspace/src/github.com/golang/glog/LICENSE

@@ -0,0 +1,191 @@
+Apache License
+Version 2.0, January 2004
+http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+"License" shall mean the terms and conditions for use, reproduction, and
+distribution as defined by Sections 1 through 9 of this document.
+
+"Licensor" shall mean the copyright owner or entity authorized by the copyright
+owner that is granting the License.
+
+"Legal Entity" shall mean the union of the acting entity and all other entities
+that control, are controlled by, or are under common control with that entity.
+For the purposes of this definition, "control" means (i) the power, direct or
+indirect, to cause the direction or management of such entity, whether by
+contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
+outstanding shares, or (iii) beneficial ownership of such entity.
+
+"You" (or "Your") shall mean an individual or Legal Entity exercising
+permissions granted by this License.
+
+"Source" form shall mean the preferred form for making modifications, including
+but not limited to software source code, documentation source, and configuration
+files.
+
+"Object" form shall mean any form resulting from mechanical transformation or
+translation of a Source form, including but not limited to compiled object code,
+generated documentation, and conversions to other media types.
+
+"Work" shall mean the work of authorship, whether in Source or Object form, made
+available under the License, as indicated by a copyright notice that is included
+in or attached to the work (an example is provided in the Appendix below).
+
+"Derivative Works" shall mean any work, whether in Source or Object form, that
+is based on (or derived from) the Work and for which the editorial revisions,
+annotations, elaborations, or other modifications represent, as a whole, an
+original work of authorship. For the purposes of this License, Derivative Works
+shall not include works that remain separable from, or merely link (or bind by
+name) to the interfaces of, the Work and Derivative Works thereof.
+
+"Contribution" shall mean any work of authorship, including the original version
+of the Work and any modifications or additions to that Work or Derivative Works
+thereof, that is intentionally submitted to Licensor for inclusion in the Work
+by the copyright owner or by an individual or Legal Entity authorized to submit
+on behalf of the copyright owner. For the purposes of this definition,
+"submitted" means any form of electronic, verbal, or written communication sent
+to the Licensor or its representatives, including but not limited to
+communication on electronic mailing lists, source code control systems, and
+issue tracking systems that are managed by, or on behalf of, the Licensor for
+the purpose of discussing and improving the Work, but excluding communication
+that is conspicuously marked or otherwise designated in writing by the copyright
+owner as "Not a Contribution."
+
+"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
+of whom a Contribution has been received by Licensor and subsequently
+incorporated within the Work.
+
+2. Grant of Copyright License.
+
+Subject to the terms and conditions of this License, each Contributor hereby
+grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
+irrevocable copyright license to reproduce, prepare Derivative Works of,
+publicly display, publicly perform, sublicense, and distribute the Work and such
+Derivative Works in Source or Object form.
+
+3. Grant of Patent License.
+
+Subject to the terms and conditions of this License, each Contributor hereby
+grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
+irrevocable (except as stated in this section) patent license to make, have
+made, use, offer to sell, sell, import, and otherwise transfer the Work, where
+such license applies only to those patent claims licensable by such Contributor
+that are necessarily infringed by their Contribution(s) alone or by combination
+of their Contribution(s) with the Work to which such Contribution(s) was
+submitted. If You institute patent litigation against any entity (including a
+cross-claim or counterclaim in a lawsuit) alleging that the Work or a
+Contribution incorporated within the Work constitutes direct or contributory
+patent infringement, then any patent licenses granted to You under this License
+for that Work shall terminate as of the date such litigation is filed.
+
+4. Redistribution.
+
+You may reproduce and distribute copies of the Work or Derivative Works thereof
+in any medium, with or without modifications, and in Source or Object form,
+provided that You meet the following conditions:
+
+You must give any other recipients of the Work or Derivative Works a copy of
+this License; and
+You must cause any modified files to carry prominent notices stating that You
+changed the files; and
+You must retain, in the Source form of any Derivative Works that You distribute,
+all copyright, patent, trademark, and attribution notices from the Source form
+of the Work, excluding those notices that do not pertain to any part of the
+Derivative Works; and
+If the Work includes a "NOTICE" text file as part of its distribution, then any
+Derivative Works that You distribute must include a readable copy of the
+attribution notices contained within such NOTICE file, excluding those notices
+that do not pertain to any part of the Derivative Works, in at least one of the
+following places: within a NOTICE text file distributed as part of the
+Derivative Works; within the Source form or documentation, if provided along
+with the Derivative Works; or, within a display generated by the Derivative
+Works, if and wherever such third-party notices normally appear. The contents of
+the NOTICE file are for informational purposes only and do not modify the
+License. You may add Your own attribution notices within Derivative Works that
+You distribute, alongside or as an addendum to the NOTICE text from the Work,
+provided that such additional attribution notices cannot be construed as
+modifying the License.
+You may add Your own copyright statement to Your modifications and may provide
+additional or different license terms and conditions for use, reproduction, or
+distribution of Your modifications, or for any such Derivative Works as a whole,
+provided Your use, reproduction, and distribution of the Work otherwise complies
+with the conditions stated in this License.
+
+5. Submission of Contributions.
+
+Unless You explicitly state otherwise, any Contribution intentionally submitted
+for inclusion in the Work by You to the Licensor shall be under the terms and
+conditions of this License, without any additional terms or conditions.
+Notwithstanding the above, nothing herein shall supersede or modify the terms of
+any separate license agreement you may have executed with Licensor regarding
+such Contributions.
+
+6. Trademarks.
+
+This License does not grant permission to use the trade names, trademarks,
+service marks, or product names of the Licensor, except as required for
+reasonable and customary use in describing the origin of the Work and
+reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty.
+
+Unless required by applicable law or agreed to in writing, Licensor provides the
+Work (and each Contributor provides its Contributions) on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
+including, without limitation, any warranties or conditions of TITLE,
+NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
+solely responsible for determining the appropriateness of using or
+redistributing the Work and assume any risks associated with Your exercise of
+permissions under this License.
+
+8. Limitation of Liability.
+
+In no event and under no legal theory, whether in tort (including negligence),
+contract, or otherwise, unless required by applicable law (such as deliberate
+and grossly negligent acts) or agreed to in writing, shall any Contributor be
+liable to You for damages, including any direct, indirect, special, incidental,
+or consequential damages of any character arising as a result of this License or
+out of the use or inability to use the Work (including but not limited to
+damages for loss of goodwill, work stoppage, computer failure or malfunction, or
+any and all other commercial damages or losses), even if such Contributor has
+been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability.
+
+While redistributing the Work or Derivative Works thereof, You may choose to
+offer, and charge a fee for, acceptance of support, warranty, indemnity, or
+other liability obligations and/or rights consistent with this License. However,
+in accepting such obligations, You may act only on Your own behalf and on Your
+sole responsibility, not on behalf of any other Contributor, and only if You
+agree to indemnify, defend, and hold each Contributor harmless for any liability
+incurred by, or claims asserted against, such Contributor by reason of your
+accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work
+
+To apply the Apache License to your work, attach the following boilerplate
+notice, with the fields enclosed by brackets "[]" replaced with your own
+identifying information. (Don't include the brackets!) The text should be
+enclosed in the appropriate comment syntax for the file format. We also
+recommend that a file or class name and description of purpose be included on
+the same "printed page" as the copyright notice for easier identification within
+third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.

+ 44 - 0
Godeps/_workspace/src/github.com/golang/glog/README

@@ -0,0 +1,44 @@
+glog
+====
+
+Leveled execution logs for Go.
+
+This is an efficient pure Go implementation of leveled logs in the
+manner of the open source C++ package
+	http://code.google.com/p/google-glog
+
+By binding methods to booleans it is possible to use the log package
+without paying the expense of evaluating the arguments to the log.
+Through the -vmodule flag, the package also provides fine-grained
+control over logging at the file level.
+
+The comment from glog.go introduces the ideas:
+
+	Package glog implements logging analogous to the Google-internal
+	C++ INFO/ERROR/V setup.  It provides functions Info, Warning,
+	Error, Fatal, plus formatting variants such as Infof. It
+	also provides V-style logging controlled by the -v and
+	-vmodule=file=2 flags.
+	
+	Basic examples:
+	
+		glog.Info("Prepare to repel boarders")
+	
+		glog.Fatalf("Initialization failed: %s", err)
+	
+	See the documentation for the V function for an explanation
+	of these examples:
+	
+		if glog.V(2) {
+			glog.Info("Starting transaction...")
+		}
+	
+		glog.V(2).Infoln("Processed", nItems, "elements")
+
+
+The repository contains an open source version of the log package
+used inside Google. The master copy of the source lives inside
+Google, not here. The code in this repo is for export only and is not itself
+under development. Feature requests will be ignored.
+
+Send bug reports to golang-nuts@googlegroups.com.

+ 1034 - 0
Godeps/_workspace/src/github.com/golang/glog/glog.go

@@ -0,0 +1,1034 @@
+// Go support for leveled logs, analogous to https://code.google.com/p/google-glog/
+//
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Package glog implements logging analogous to the Google-internal C++ INFO/ERROR/V setup.
+// It provides functions Info, Warning, Error, Fatal, plus formatting variants such as
+// Infof. It also provides V-style logging controlled by the -v and -vmodule=file=2 flags.
+//
+// Basic examples:
+//
+//	glog.Info("Prepare to repel boarders")
+//
+//	glog.Fatalf("Initialization failed: %s", err)
+//
+// See the documentation for the V function for an explanation of these examples:
+//
+//	if glog.V(2) {
+//		glog.Info("Starting transaction...")
+//	}
+//
+//	glog.V(2).Infoln("Processed", nItems, "elements")
+//
+// Log output is buffered and written periodically using Flush. Programs
+// should call Flush before exiting to guarantee all log output is written.
+//
+// By default, all log statements write to files in a temporary directory.
+// This package provides several flags that modify this behavior.
+// As a result, flag.Parse must be called before any logging is done.
+//
+//	-logtostderr=false
+//		Logs are written to standard error instead of to files.
+//	-alsologtostderr=false
+//		Logs are written to standard error as well as to files.
+//	-stderrthreshold=ERROR
+//		Log events at or above this severity are logged to standard
+//		error as well as to files.
+//	-log_dir=""
+//		Log files will be written to this directory instead of the
+//		default temporary directory.
+//
+//	Other flags provide aids to debugging.
+//
+//	-log_backtrace_at=""
+//		When set to a file and line number holding a logging statement,
+//		such as
+//			-log_backtrace_at=gopherflakes.go:234
+//		a stack trace will be written to the Info log whenever execution
+//		hits that statement. (Unlike with -vmodule, the ".go" must be
+//		present.)
+//	-v=0
+//		Enable V-leveled logging at the specified level.
+//	-vmodule=""
+//		The syntax of the argument is a comma-separated list of pattern=N,
+//		where pattern is a literal file name (minus the ".go" suffix) or
+//		"glob" pattern and N is a V level. For instance,
+//			-vmodule=gopher*=3
+//		sets the V level to 3 in all Go files whose names begin "gopher".
+//
+package glog
+
+import (
+	"bufio"
+	"bytes"
+	"errors"
+	"flag"
+	"fmt"
+	"io"
+	"os"
+	"path/filepath"
+	"runtime"
+	"strconv"
+	"strings"
+	"sync"
+	"sync/atomic"
+	"time"
+)
+
+// severity identifies the sort of log: info, warning etc. It also implements
+// the flag.Value interface. The -stderrthreshold flag is of type severity and
+// should be modified only through the flag.Value interface. The values match
+// the corresponding constants in C++.
+type severity int32 // sync/atomic int32
+
+const (
+	infoLog severity = iota
+	warningLog
+	errorLog
+	fatalLog
+	numSeverity = 4
+)
+
+const severityChar = "IWEF"
+
+var severityName = []string{
+	infoLog:    "INFO",
+	warningLog: "WARNING",
+	errorLog:   "ERROR",
+	fatalLog:   "FATAL",
+}
+
+// get returns the value of the severity.
+func (s *severity) get() severity {
+	return severity(atomic.LoadInt32((*int32)(s)))
+}
+
+// set sets the value of the severity.
+func (s *severity) set(val severity) {
+	atomic.StoreInt32((*int32)(s), int32(val))
+}
+
+// String is part of the flag.Value interface.
+func (s *severity) String() string {
+	return strconv.FormatInt(int64(*s), 10)
+}
+
+// Get is part of the flag.Value interface.
+func (s *severity) Get() interface{} {
+	return *s
+}
+
+// Set is part of the flag.Value interface.
+func (s *severity) Set(value string) error {
+	var threshold severity
+	// Is it a known name?
+	if v, ok := severityByName(value); ok {
+		threshold = v
+	} else {
+		v, err := strconv.Atoi(value)
+		if err != nil {
+			return err
+		}
+		threshold = severity(v)
+	}
+	logging.stderrThreshold.set(threshold)
+	return nil
+}
+
+func severityByName(s string) (severity, bool) {
+	s = strings.ToUpper(s)
+	for i, name := range severityName {
+		if name == s {
+			return severity(i), true
+		}
+	}
+	return 0, false
+}
+
+// OutputStats tracks the number of output lines and bytes written.
+type OutputStats struct {
+	lines int64
+	bytes int64
+}
+
+// Lines returns the number of lines written.
+func (s *OutputStats) Lines() int64 {
+	return atomic.LoadInt64(&s.lines)
+}
+
+// Bytes returns the number of bytes written.
+func (s *OutputStats) Bytes() int64 {
+	return atomic.LoadInt64(&s.bytes)
+}
+
+// Stats tracks the number of lines of output and number of bytes
+// per severity level. Values must be read with atomic.LoadInt64.
+var Stats struct {
+	Info, Warning, Error OutputStats
+}
+
+var severityStats = [numSeverity]*OutputStats{
+	infoLog:    &Stats.Info,
+	warningLog: &Stats.Warning,
+	errorLog:   &Stats.Error,
+}
+
+// Level is exported because it appears in the arguments to V and is
+// the type of the v flag, which can be set programmatically.
+// It's a distinct type because we want to discriminate it from logType.
+// Variables of type level are only changed under logging.mu.
+// The -v flag is read only with atomic ops, so the state of the logging
+// module is consistent.
+
+// Level is treated as a sync/atomic int32.
+
+// Level specifies a level of verbosity for V logs. *Level implements
+// flag.Value; the -v flag is of type Level and should be modified
+// only through the flag.Value interface.
+type Level int32
+
+// get returns the value of the Level.
+func (l *Level) get() Level {
+	return Level(atomic.LoadInt32((*int32)(l)))
+}
+
+// set sets the value of the Level.
+func (l *Level) set(val Level) {
+	atomic.StoreInt32((*int32)(l), int32(val))
+}
+
+// String is part of the flag.Value interface.
+func (l *Level) String() string {
+	return strconv.FormatInt(int64(*l), 10)
+}
+
+// Get is part of the flag.Value interface.
+func (l *Level) Get() interface{} {
+	return *l
+}
+
+// Set is part of the flag.Value interface.
+func (l *Level) Set(value string) error {
+	v, err := strconv.Atoi(value)
+	if err != nil {
+		return err
+	}
+	logging.mu.Lock()
+	defer logging.mu.Unlock()
+	logging.setVState(Level(v), logging.vmodule.filter, false)
+	return nil
+}
+
+// moduleSpec represents the setting of the -vmodule flag.
+type moduleSpec struct {
+	filter []modulePat
+}
+
+// modulePat contains a filter for the -vmodule flag.
+// It holds a verbosity level and a file pattern to match.
+type modulePat struct {
+	pattern string
+	literal bool // The pattern is a literal string
+	level   Level
+}
+
+// match reports whether the file matches the pattern. It uses a string
+// comparison if the pattern contains no metacharacters.
+func (m *modulePat) match(file string) bool {
+	if m.literal {
+		return file == m.pattern
+	}
+	match, _ := filepath.Match(m.pattern, file)
+	return match
+}
+
+func (m *moduleSpec) String() string {
+	// Lock because the type is not atomic. TODO: clean this up.
+	logging.mu.Lock()
+	defer logging.mu.Unlock()
+	var b bytes.Buffer
+	for i, f := range m.filter {
+		if i > 0 {
+			b.WriteRune(',')
+		}
+		fmt.Fprintf(&b, "%s=%d", f.pattern, f.level)
+	}
+	return b.String()
+}
+
+// Get is part of the (Go 1.2)  flag.Getter interface. It always returns nil for this flag type since the
+// struct is not exported.
+func (m *moduleSpec) Get() interface{} {
+	return nil
+}
+
+var errVmoduleSyntax = errors.New("syntax error: expect comma-separated list of filename=N")
+
+// Syntax: -vmodule=recordio=2,file=1,gfs*=3
+func (m *moduleSpec) Set(value string) error {
+	var filter []modulePat
+	for _, pat := range strings.Split(value, ",") {
+		if len(pat) == 0 {
+			// Empty strings such as from a trailing comma can be ignored.
+			continue
+		}
+		patLev := strings.Split(pat, "=")
+		if len(patLev) != 2 || len(patLev[0]) == 0 || len(patLev[1]) == 0 {
+			return errVmoduleSyntax
+		}
+		pattern := patLev[0]
+		v, err := strconv.Atoi(patLev[1])
+		if err != nil {
+			return errors.New("syntax error: expect comma-separated list of filename=N")
+		}
+		if v < 0 {
+			return errors.New("negative value for vmodule level")
+		}
+		if v == 0 {
+			continue // Ignore. It's harmless but no point in paying the overhead.
+		}
+		// TODO: check syntax of filter?
+		filter = append(filter, modulePat{pattern, isLiteral(pattern), Level(v)})
+	}
+	logging.mu.Lock()
+	defer logging.mu.Unlock()
+	logging.setVState(logging.verbosity, filter, true)
+	return nil
+}
+
+// isLiteral reports whether the pattern is a literal string, that is, has no metacharacters
+// that require filepath.Match to be called to match the pattern.
+func isLiteral(pattern string) bool {
+	return !strings.ContainsAny(pattern, `*?[]\`)
+}
+
+// traceLocation represents the setting of the -log_backtrace_at flag.
+type traceLocation struct {
+	file string
+	line int
+}
+
+// isSet reports whether the trace location has been specified.
+// logging.mu is held.
+func (t *traceLocation) isSet() bool {
+	return t.line > 0
+}
+
+// match reports whether the specified file and line matches the trace location.
+// The argument file name is the full path, not the basename specified in the flag.
+// logging.mu is held.
+func (t *traceLocation) match(file string, line int) bool {
+	if t.line != line {
+		return false
+	}
+	if i := strings.LastIndex(file, "/"); i >= 0 {
+		file = file[i+1:]
+	}
+	return t.file == file
+}
+
+func (t *traceLocation) String() string {
+	// Lock because the type is not atomic. TODO: clean this up.
+	logging.mu.Lock()
+	defer logging.mu.Unlock()
+	return fmt.Sprintf("%s:%d", t.file, t.line)
+}
+
+// Get is part of the (Go 1.2) flag.Getter interface. It always returns nil for this flag type since the
+// struct is not exported
+func (t *traceLocation) Get() interface{} {
+	return nil
+}
+
+var errTraceSyntax = errors.New("syntax error: expect file.go:234")
+
+// Syntax: -log_backtrace_at=gopherflakes.go:234
+// Note that unlike vmodule the file extension is included here.
+func (t *traceLocation) Set(value string) error {
+	if value == "" {
+		// Unset.
+		t.line = 0
+		t.file = ""
+	}
+	fields := strings.Split(value, ":")
+	if len(fields) != 2 {
+		return errTraceSyntax
+	}
+	file, line := fields[0], fields[1]
+	if !strings.Contains(file, ".") {
+		return errTraceSyntax
+	}
+	v, err := strconv.Atoi(line)
+	if err != nil {
+		return errTraceSyntax
+	}
+	if v <= 0 {
+		return errors.New("negative or zero value for level")
+	}
+	logging.mu.Lock()
+	defer logging.mu.Unlock()
+	t.line = v
+	t.file = file
+	return nil
+}
+
+// flushSyncWriter is the interface satisfied by logging destinations.
+type flushSyncWriter interface {
+	Flush() error
+	Sync() error
+	io.Writer
+}
+
+func init() {
+	flag.BoolVar(&logging.toStderr, "logtostderr", false, "log to standard error instead of files")
+	flag.BoolVar(&logging.alsoToStderr, "alsologtostderr", false, "log to standard error as well as files")
+	flag.Var(&logging.verbosity, "v", "log level for V logs")
+	flag.Var(&logging.stderrThreshold, "stderrthreshold", "logs at or above this threshold go to stderr")
+	flag.Var(&logging.vmodule, "vmodule", "comma-separated list of pattern=N settings for file-filtered logging")
+	flag.Var(&logging.traceLocation, "log_backtrace_at", "when logging hits line file:N, emit a stack trace")
+
+	// Default stderrThreshold is ERROR.
+	logging.stderrThreshold = errorLog
+
+	logging.setVState(0, nil, false)
+	go logging.flushDaemon()
+}
+
+// Flush flushes all pending log I/O.
+func Flush() {
+	logging.lockAndFlushAll()
+}
+
+// loggingT collects all the global state of the logging setup.
+type loggingT struct {
+	// Boolean flags. Not handled atomically because the flag.Value interface
+	// does not let us avoid the =true, and that shorthand is necessary for
+	// compatibility. TODO: does this matter enough to fix? Seems unlikely.
+	toStderr     bool // The -logtostderr flag.
+	alsoToStderr bool // The -alsologtostderr flag.
+
+	// Level flag. Handled atomically.
+	stderrThreshold severity // The -stderrthreshold flag.
+
+	// freeList is a list of byte buffers, maintained under freeListMu.
+	freeList *buffer
+	// freeListMu maintains the free list. It is separate from the main mutex
+	// so buffers can be grabbed and printed to without holding the main lock,
+	// for better parallelization.
+	freeListMu sync.Mutex
+
+	// mu protects the remaining elements of this structure and is
+	// used to synchronize logging.
+	mu sync.Mutex
+	// file holds writer for each of the log types.
+	file [numSeverity]flushSyncWriter
+	// pcs is used in V to avoid an allocation when computing the caller's PC.
+	pcs [1]uintptr
+	// vmap is a cache of the V Level for each V() call site, identified by PC.
+	// It is wiped whenever the vmodule flag changes state.
+	vmap map[uintptr]Level
+	// filterLength stores the length of the vmodule filter chain. If greater
+	// than zero, it means vmodule is enabled. It may be read safely
+	// using sync.LoadInt32, but is only modified under mu.
+	filterLength int32
+	// traceLocation is the state of the -log_backtrace_at flag.
+	traceLocation traceLocation
+	// These flags are modified only under lock, although verbosity may be fetched
+	// safely using atomic.LoadInt32.
+	vmodule   moduleSpec // The state of the -vmodule flag.
+	verbosity Level      // V logging level, the value of the -v flag/
+}
+
+// buffer holds a byte Buffer for reuse. The zero value is ready for use.
+type buffer struct {
+	bytes.Buffer
+	tmp  [64]byte // temporary byte array for creating headers.
+	next *buffer
+}
+
+var logging loggingT
+
+// setVState sets a consistent state for V logging.
+// l.mu is held.
+func (l *loggingT) setVState(verbosity Level, filter []modulePat, setFilter bool) {
+	// Turn verbosity off so V will not fire while we are in transition.
+	logging.verbosity.set(0)
+	// Ditto for filter length.
+	logging.filterLength = 0
+
+	// Set the new filters and wipe the pc->Level map if the filter has changed.
+	if setFilter {
+		logging.vmodule.filter = filter
+		logging.vmap = make(map[uintptr]Level)
+	}
+
+	// Things are consistent now, so enable filtering and verbosity.
+	// They are enabled in order opposite to that in V.
+	atomic.StoreInt32(&logging.filterLength, int32(len(filter)))
+	logging.verbosity.set(verbosity)
+}
+
+// getBuffer returns a new, ready-to-use buffer.
+func (l *loggingT) getBuffer() *buffer {
+	l.freeListMu.Lock()
+	b := l.freeList
+	if b != nil {
+		l.freeList = b.next
+	}
+	l.freeListMu.Unlock()
+	if b == nil {
+		b = new(buffer)
+	} else {
+		b.next = nil
+		b.Reset()
+	}
+	return b
+}
+
+// putBuffer returns a buffer to the free list.
+func (l *loggingT) putBuffer(b *buffer) {
+	if b.Len() >= 256 {
+		// Let big buffers die a natural death.
+		return
+	}
+	l.freeListMu.Lock()
+	b.next = l.freeList
+	l.freeList = b
+	l.freeListMu.Unlock()
+}
+
+var timeNow = time.Now // Stubbed out for testing.
+
+/*
+header formats a log header as defined by the C++ implementation.
+It returns a buffer containing the formatted header.
+
+Log lines have this form:
+	Lmmdd hh:mm:ss.uuuuuu threadid file:line] msg...
+where the fields are defined as follows:
+	L                A single character, representing the log level (eg 'I' for INFO)
+	mm               The month (zero padded; ie May is '05')
+	dd               The day (zero padded)
+	hh:mm:ss.uuuuuu  Time in hours, minutes and fractional seconds
+	threadid         The space-padded thread ID as returned by GetTID()
+	file             The file name
+	line             The line number
+	msg              The user-supplied message
+*/
+func (l *loggingT) header(s severity) *buffer {
+	// Lmmdd hh:mm:ss.uuuuuu threadid file:line]
+	now := timeNow()
+	_, file, line, ok := runtime.Caller(3) // It's always the same number of frames to the user's call.
+	if !ok {
+		file = "???"
+		line = 1
+	} else {
+		slash := strings.LastIndex(file, "/")
+		if slash >= 0 {
+			file = file[slash+1:]
+		}
+	}
+	if line < 0 {
+		line = 0 // not a real line number, but acceptable to someDigits
+	}
+	if s > fatalLog {
+		s = infoLog // for safety.
+	}
+	buf := l.getBuffer()
+
+	// Avoid Fprintf, for speed. The format is so simple that we can do it quickly by hand.
+	// It's worth about 3X. Fprintf is hard.
+	_, month, day := now.Date()
+	hour, minute, second := now.Clock()
+	buf.tmp[0] = severityChar[s]
+	buf.twoDigits(1, int(month))
+	buf.twoDigits(3, day)
+	buf.tmp[5] = ' '
+	buf.twoDigits(6, hour)
+	buf.tmp[8] = ':'
+	buf.twoDigits(9, minute)
+	buf.tmp[11] = ':'
+	buf.twoDigits(12, second)
+	buf.tmp[14] = '.'
+	buf.nDigits(6, 15, now.Nanosecond()/1000)
+	buf.tmp[21] = ' '
+	buf.nDigits(5, 22, pid) // TODO: should be TID
+	buf.tmp[27] = ' '
+	buf.Write(buf.tmp[:28])
+	buf.WriteString(file)
+	buf.tmp[0] = ':'
+	n := buf.someDigits(1, line)
+	buf.tmp[n+1] = ']'
+	buf.tmp[n+2] = ' '
+	buf.Write(buf.tmp[:n+3])
+	return buf
+}
+
+// Some custom tiny helper functions to print the log header efficiently.
+
+const digits = "0123456789"
+
+// twoDigits formats a zero-prefixed two-digit integer at buf.tmp[i].
+func (buf *buffer) twoDigits(i, d int) {
+	buf.tmp[i+1] = digits[d%10]
+	d /= 10
+	buf.tmp[i] = digits[d%10]
+}
+
+// nDigits formats a zero-prefixed n-digit integer at buf.tmp[i].
+func (buf *buffer) nDigits(n, i, d int) {
+	for j := n - 1; j >= 0; j-- {
+		buf.tmp[i+j] = digits[d%10]
+		d /= 10
+	}
+}
+
+// someDigits formats a zero-prefixed variable-width integer at buf.tmp[i].
+func (buf *buffer) someDigits(i, d int) int {
+	// Print into the top, then copy down. We know there's space for at least
+	// a 10-digit number.
+	j := len(buf.tmp)
+	for {
+		j--
+		buf.tmp[j] = digits[d%10]
+		d /= 10
+		if d == 0 {
+			break
+		}
+	}
+	return copy(buf.tmp[i:], buf.tmp[j:])
+}
+
+func (l *loggingT) println(s severity, args ...interface{}) {
+	buf := l.header(s)
+	fmt.Fprintln(buf, args...)
+	l.output(s, buf)
+}
+
+func (l *loggingT) print(s severity, args ...interface{}) {
+	buf := l.header(s)
+	fmt.Fprint(buf, args...)
+	if buf.Bytes()[buf.Len()-1] != '\n' {
+		buf.WriteByte('\n')
+	}
+	l.output(s, buf)
+}
+
+func (l *loggingT) printf(s severity, format string, args ...interface{}) {
+	buf := l.header(s)
+	fmt.Fprintf(buf, format, args...)
+	if buf.Bytes()[buf.Len()-1] != '\n' {
+		buf.WriteByte('\n')
+	}
+	l.output(s, buf)
+}
+
+// output writes the data to the log files and releases the buffer.
+func (l *loggingT) output(s severity, buf *buffer) {
+	l.mu.Lock()
+	if l.traceLocation.isSet() {
+		_, file, line, ok := runtime.Caller(3) // It's always the same number of frames to the user's call (same as header).
+		if ok && l.traceLocation.match(file, line) {
+			buf.Write(stacks(false))
+		}
+	}
+	data := buf.Bytes()
+	if l.toStderr {
+		os.Stderr.Write(data)
+	} else {
+		if l.alsoToStderr || s >= l.stderrThreshold.get() {
+			os.Stderr.Write(data)
+		}
+		if l.file[s] == nil {
+			if err := l.createFiles(s); err != nil {
+				os.Stderr.Write(data) // Make sure the message appears somewhere.
+				l.exit(err)
+			}
+		}
+		switch s {
+		case fatalLog:
+			l.file[fatalLog].Write(data)
+			fallthrough
+		case errorLog:
+			l.file[errorLog].Write(data)
+			fallthrough
+		case warningLog:
+			l.file[warningLog].Write(data)
+			fallthrough
+		case infoLog:
+			l.file[infoLog].Write(data)
+		}
+	}
+	if s == fatalLog {
+		// Make sure we see the trace for the current goroutine on standard error.
+		if !l.toStderr {
+			os.Stderr.Write(stacks(false))
+		}
+		// Write the stack trace for all goroutines to the files.
+		trace := stacks(true)
+		logExitFunc = func(error) {} // If we get a write error, we'll still exit below.
+		for log := fatalLog; log >= infoLog; log-- {
+			if f := l.file[log]; f != nil { // Can be nil if -logtostderr is set.
+				f.Write(trace)
+			}
+		}
+		l.mu.Unlock()
+		timeoutFlush(10 * time.Second)
+		os.Exit(255) // C++ uses -1, which is silly because it's anded with 255 anyway.
+	}
+	l.putBuffer(buf)
+	l.mu.Unlock()
+	if stats := severityStats[s]; stats != nil {
+		atomic.AddInt64(&stats.lines, 1)
+		atomic.AddInt64(&stats.bytes, int64(len(data)))
+	}
+}
+
+// timeoutFlush calls Flush and returns when it completes or after timeout
+// elapses, whichever happens first.  This is needed because the hooks invoked
+// by Flush may deadlock when glog.Fatal is called from a hook that holds
+// a lock.
+func timeoutFlush(timeout time.Duration) {
+	done := make(chan bool, 1)
+	go func() {
+		Flush() // calls logging.lockAndFlushAll()
+		done <- true
+	}()
+	select {
+	case <-done:
+	case <-time.After(timeout):
+		fmt.Fprintln(os.Stderr, "glog: Flush took longer than", timeout)
+	}
+}
+
+// stacks is a wrapper for runtime.Stack that attempts to recover the data for all goroutines.
+func stacks(all bool) []byte {
+	// We don't know how big the traces are, so grow a few times if they don't fit. Start large, though.
+	n := 10000
+	if all {
+		n = 100000
+	}
+	var trace []byte
+	for i := 0; i < 5; i++ {
+		trace = make([]byte, n)
+		nbytes := runtime.Stack(trace, all)
+		if nbytes < len(trace) {
+			return trace[:nbytes]
+		}
+		n *= 2
+	}
+	return trace
+}
+
+// logExitFunc provides a simple mechanism to override the default behavior
+// of exiting on error. Used in testing and to guarantee we reach a required exit
+// for fatal logs. Instead, exit could be a function rather than a method but that
+// would make its use clumsier.
+var logExitFunc func(error)
+
+// exit is called if there is trouble creating or writing log files.
+// It flushes the logs and exits the program; there's no point in hanging around.
+// l.mu is held.
+func (l *loggingT) exit(err error) {
+	fmt.Fprintf(os.Stderr, "log: exiting because of error: %s\n", err)
+	// If logExitFunc is set, we do that instead of exiting.
+	if logExitFunc != nil {
+		logExitFunc(err)
+		return
+	}
+	l.flushAll()
+	os.Exit(2)
+}
+
+// syncBuffer joins a bufio.Writer to its underlying file, providing access to the
+// file's Sync method and providing a wrapper for the Write method that provides log
+// file rotation. There are conflicting methods, so the file cannot be embedded.
+// l.mu is held for all its methods.
+type syncBuffer struct {
+	logger *loggingT
+	*bufio.Writer
+	file   *os.File
+	sev    severity
+	nbytes uint64 // The number of bytes written to this file
+}
+
+func (sb *syncBuffer) Sync() error {
+	return sb.file.Sync()
+}
+
+func (sb *syncBuffer) Write(p []byte) (n int, err error) {
+	if sb.nbytes+uint64(len(p)) >= MaxSize {
+		if err := sb.rotateFile(time.Now()); err != nil {
+			sb.logger.exit(err)
+		}
+	}
+	n, err = sb.Writer.Write(p)
+	sb.nbytes += uint64(n)
+	if err != nil {
+		sb.logger.exit(err)
+	}
+	return
+}
+
+// rotateFile closes the syncBuffer's file and starts a new one.
+func (sb *syncBuffer) rotateFile(now time.Time) error {
+	if sb.file != nil {
+		sb.Flush()
+		sb.file.Close()
+	}
+	var err error
+	sb.file, _, err = create(severityName[sb.sev], now)
+	sb.nbytes = 0
+	if err != nil {
+		return err
+	}
+
+	sb.Writer = bufio.NewWriterSize(sb.file, bufferSize)
+
+	// Write header.
+	var buf bytes.Buffer
+	fmt.Fprintf(&buf, "Log file created at: %s\n", now.Format("2006/01/02 15:04:05"))
+	fmt.Fprintf(&buf, "Running on machine: %s\n", host)
+	fmt.Fprintf(&buf, "Binary: Built with %s %s for %s/%s\n", runtime.Compiler, runtime.Version(), runtime.GOOS, runtime.GOARCH)
+	fmt.Fprintf(&buf, "Log line format: [IWEF]mmdd hh:mm:ss.uuuuuu threadid file:line] msg\n")
+	n, err := sb.file.Write(buf.Bytes())
+	sb.nbytes += uint64(n)
+	return err
+}
+
+// bufferSize sizes the buffer associated with each log file. It's large
+// so that log records can accumulate without the logging thread blocking
+// on disk I/O. The flushDaemon will block instead.
+const bufferSize = 256 * 1024
+
+// createFiles creates all the log files for severity from sev down to infoLog.
+// l.mu is held.
+func (l *loggingT) createFiles(sev severity) error {
+	now := time.Now()
+	// Files are created in decreasing severity order, so as soon as we find one
+	// has already been created, we can stop.
+	for s := sev; s >= infoLog && l.file[s] == nil; s-- {
+		sb := &syncBuffer{
+			logger: l,
+			sev:    s,
+		}
+		if err := sb.rotateFile(now); err != nil {
+			return err
+		}
+		l.file[s] = sb
+	}
+	return nil
+}
+
+const flushInterval = 30 * time.Second
+
+// flushDaemon periodically flushes the log file buffers.
+func (l *loggingT) flushDaemon() {
+	for _ = range time.NewTicker(flushInterval).C {
+		l.lockAndFlushAll()
+	}
+}
+
+// lockAndFlushAll is like flushAll but locks l.mu first.
+func (l *loggingT) lockAndFlushAll() {
+	l.mu.Lock()
+	l.flushAll()
+	l.mu.Unlock()
+}
+
+// flushAll flushes all the logs and attempts to "sync" their data to disk.
+// l.mu is held.
+func (l *loggingT) flushAll() {
+	// Flush from fatal down, in case there's trouble flushing.
+	for s := fatalLog; s >= infoLog; s-- {
+		file := l.file[s]
+		if file != nil {
+			file.Flush() // ignore error
+			file.Sync()  // ignore error
+		}
+	}
+}
+
+// setV computes and remembers the V level for a given PC
+// when vmodule is enabled.
+// File pattern matching takes the basename of the file, stripped
+// of its .go suffix, and uses filepath.Match, which is a little more
+// general than the *? matching used in C++.
+// l.mu is held.
+func (l *loggingT) setV(pc uintptr) Level {
+	fn := runtime.FuncForPC(pc)
+	file, _ := fn.FileLine(pc)
+	// The file is something like /a/b/c/d.go. We want just the d.
+	if strings.HasSuffix(file, ".go") {
+		file = file[:len(file)-3]
+	}
+	if slash := strings.LastIndex(file, "/"); slash >= 0 {
+		file = file[slash+1:]
+	}
+	for _, filter := range l.vmodule.filter {
+		if filter.match(file) {
+			l.vmap[pc] = filter.level
+			return filter.level
+		}
+	}
+	l.vmap[pc] = 0
+	return 0
+}
+
+// Verbose is a boolean type that implements Infof (like Printf) etc.
+// See the documentation of V for more information.
+type Verbose bool
+
+// V reports whether verbosity at the call site is at least the requested level.
+// The returned value is a boolean of type Verbose, which implements Info, Infoln
+// and Infof. These methods will write to the Info log if called.
+// Thus, one may write either
+//	if glog.V(2) { glog.Info("log this") }
+// or
+//	glog.V(2).Info("log this")
+// The second form is shorter but the first is cheaper if logging is off because it does
+// not evaluate its arguments.
+//
+// Whether an individual call to V generates a log record depends on the setting of
+// the -v and --vmodule flags; both are off by default. If the level in the call to
+// V is at least the value of -v, or of -vmodule for the source file containing the
+// call, the V call will log.
+func V(level Level) Verbose {
+	// This function tries hard to be cheap unless there's work to do.
+	// The fast path is two atomic loads and compares.
+
+	// Here is a cheap but safe test to see if V logging is enabled globally.
+	if logging.verbosity.get() >= level {
+		return Verbose(true)
+	}
+
+	// It's off globally but it vmodule may still be set.
+	// Here is another cheap but safe test to see if vmodule is enabled.
+	if atomic.LoadInt32(&logging.filterLength) > 0 {
+		// Now we need a proper lock to use the logging structure. The pcs field
+		// is shared so we must lock before accessing it. This is fairly expensive,
+		// but if V logging is enabled we're slow anyway.
+		logging.mu.Lock()
+		defer logging.mu.Unlock()
+		if runtime.Callers(2, logging.pcs[:]) == 0 {
+			return Verbose(false)
+		}
+		v, ok := logging.vmap[logging.pcs[0]]
+		if !ok {
+			v = logging.setV(logging.pcs[0])
+		}
+		return Verbose(v >= level)
+	}
+	return Verbose(false)
+}
+
+// Info is equivalent to the global Info function, guarded by the value of v.
+// See the documentation of V for usage.
+func (v Verbose) Info(args ...interface{}) {
+	if v {
+		logging.print(infoLog, args...)
+	}
+}
+
+// Infoln is equivalent to the global Infoln function, guarded by the value of v.
+// See the documentation of V for usage.
+func (v Verbose) Infoln(args ...interface{}) {
+	if v {
+		logging.println(infoLog, args...)
+	}
+}
+
+// Infof is equivalent to the global Infof function, guarded by the value of v.
+// See the documentation of V for usage.
+func (v Verbose) Infof(format string, args ...interface{}) {
+	if v {
+		logging.printf(infoLog, format, args...)
+	}
+}
+
+// Info logs to the INFO log.
+// Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
+func Info(args ...interface{}) {
+	logging.print(infoLog, args...)
+}
+
+// Infoln logs to the INFO log.
+// Arguments are handled in the manner of fmt.Println; a newline is appended if missing.
+func Infoln(args ...interface{}) {
+	logging.println(infoLog, args...)
+}
+
+// Infof logs to the INFO log.
+// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing.
+func Infof(format string, args ...interface{}) {
+	logging.printf(infoLog, format, args...)
+}
+
+// Warning logs to the WARNING and INFO logs.
+// Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
+func Warning(args ...interface{}) {
+	logging.print(warningLog, args...)
+}
+
+// Warningln logs to the WARNING and INFO logs.
+// Arguments are handled in the manner of fmt.Println; a newline is appended if missing.
+func Warningln(args ...interface{}) {
+	logging.println(warningLog, args...)
+}
+
+// Warningf logs to the WARNING and INFO logs.
+// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing.
+func Warningf(format string, args ...interface{}) {
+	logging.printf(warningLog, format, args...)
+}
+
+// Error logs to the ERROR, WARNING, and INFO logs.
+// Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
+func Error(args ...interface{}) {
+	logging.print(errorLog, args...)
+}
+
+// Errorln logs to the ERROR, WARNING, and INFO logs.
+// Arguments are handled in the manner of fmt.Println; a newline is appended if missing.
+func Errorln(args ...interface{}) {
+	logging.println(errorLog, args...)
+}
+
+// Errorf logs to the ERROR, WARNING, and INFO logs.
+// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing.
+func Errorf(format string, args ...interface{}) {
+	logging.printf(errorLog, format, args...)
+}
+
+// Fatal logs to the FATAL, ERROR, WARNING, and INFO logs,
+// including a stack trace of all running goroutines, then calls os.Exit(255).
+// Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
+func Fatal(args ...interface{}) {
+	logging.print(fatalLog, args...)
+}
+
+// Fatalln logs to the FATAL, ERROR, WARNING, and INFO logs,
+// including a stack trace of all running goroutines, then calls os.Exit(255).
+// Arguments are handled in the manner of fmt.Println; a newline is appended if missing.
+func Fatalln(args ...interface{}) {
+	logging.println(fatalLog, args...)
+}
+
+// Fatalf logs to the FATAL, ERROR, WARNING, and INFO logs,
+// including a stack trace of all running goroutines, then calls os.Exit(255).
+// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing.
+func Fatalf(format string, args ...interface{}) {
+	logging.printf(fatalLog, format, args...)
+}

+ 124 - 0
Godeps/_workspace/src/github.com/golang/glog/glog_file.go

@@ -0,0 +1,124 @@
+// Go support for leveled logs, analogous to https://code.google.com/p/google-glog/
+//
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// File I/O for logs.
+
+package glog
+
+import (
+	"errors"
+	"flag"
+	"fmt"
+	"os"
+	"os/user"
+	"path/filepath"
+	"strings"
+	"sync"
+	"time"
+)
+
+// MaxSize is the maximum size of a log file in bytes.
+var MaxSize uint64 = 1024 * 1024 * 1800
+
+// logDirs lists the candidate directories for new log files.
+var logDirs []string
+
+// If non-empty, overrides the choice of directory in which to write logs.
+// See createLogDirs for the full list of possible destinations.
+var logDir = flag.String("log_dir", "", "If non-empty, write log files in this directory")
+
+func createLogDirs() {
+	if *logDir != "" {
+		logDirs = append(logDirs, *logDir)
+	}
+	logDirs = append(logDirs, os.TempDir())
+}
+
+var (
+	pid      = os.Getpid()
+	program  = filepath.Base(os.Args[0])
+	host     = "unknownhost"
+	userName = "unknownuser"
+)
+
+func init() {
+	h, err := os.Hostname()
+	if err == nil {
+		host = shortHostname(h)
+	}
+
+	current, err := user.Current()
+	if err == nil {
+		userName = current.Username
+	}
+
+	// Sanitize userName since it may contain filepath separators on Windows.
+	userName = strings.Replace(userName, `\`, "_", -1)
+}
+
+// shortHostname returns its argument, truncating at the first period.
+// For instance, given "www.google.com" it returns "www".
+func shortHostname(hostname string) string {
+	if i := strings.Index(hostname, "."); i >= 0 {
+		return hostname[:i]
+	}
+	return hostname
+}
+
+// logName returns a new log file name containing tag, with start time t, and
+// the name for the symlink for tag.
+func logName(tag string, t time.Time) (name, link string) {
+	name = fmt.Sprintf("%s.%s.%s.log.%s.%04d%02d%02d-%02d%02d%02d.%d",
+		program,
+		host,
+		userName,
+		tag,
+		t.Year(),
+		t.Month(),
+		t.Day(),
+		t.Hour(),
+		t.Minute(),
+		t.Second(),
+		pid)
+	return name, program + "." + tag
+}
+
+var onceLogDirs sync.Once
+
+// create creates a new log file and returns the file and its filename, which
+// contains tag ("INFO", "FATAL", etc.) and t.  If the file is created
+// successfully, create also attempts to update the symlink for that tag, ignoring
+// errors.
+func create(tag string, t time.Time) (f *os.File, filename string, err error) {
+	onceLogDirs.Do(createLogDirs)
+	if len(logDirs) == 0 {
+		return nil, "", errors.New("log: no log dirs")
+	}
+	name, link := logName(tag, t)
+	var lastErr error
+	for _, dir := range logDirs {
+		fname := filepath.Join(dir, name)
+		f, err := os.Create(fname)
+		if err == nil {
+			symlink := filepath.Join(dir, link)
+			os.Remove(symlink)        // ignore err
+			os.Symlink(name, symlink) // ignore err
+			return f, fname, nil
+		}
+		lastErr = err
+	}
+	return nil, "", fmt.Errorf("log: cannot create log: %v", lastErr)
+}

+ 333 - 0
Godeps/_workspace/src/github.com/golang/glog/glog_test.go

@@ -0,0 +1,333 @@
+// Go support for leveled logs, analogous to https://code.google.com/p/google-glog/
+//
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package glog
+
+import (
+	"bytes"
+	"fmt"
+	"path/filepath"
+	"runtime"
+	"strings"
+	"testing"
+	"time"
+)
+
+// Test that shortHostname works as advertised.
+func TestShortHostname(t *testing.T) {
+	for hostname, expect := range map[string]string{
+		"":                "",
+		"host":            "host",
+		"host.google.com": "host",
+	} {
+		if got := shortHostname(hostname); expect != got {
+			t.Errorf("shortHostname(%q): expected %q, got %q", hostname, expect, got)
+		}
+	}
+}
+
+// flushBuffer wraps a bytes.Buffer to satisfy flushSyncWriter.
+type flushBuffer struct {
+	bytes.Buffer
+}
+
+func (f *flushBuffer) Flush() error {
+	return nil
+}
+
+func (f *flushBuffer) Sync() error {
+	return nil
+}
+
+// swap sets the log writers and returns the old array.
+func (l *loggingT) swap(writers [numSeverity]flushSyncWriter) (old [numSeverity]flushSyncWriter) {
+	l.mu.Lock()
+	defer l.mu.Unlock()
+	old = l.file
+	for i, w := range writers {
+		logging.file[i] = w
+	}
+	return
+}
+
+// newBuffers sets the log writers to all new byte buffers and returns the old array.
+func (l *loggingT) newBuffers() [numSeverity]flushSyncWriter {
+	return l.swap([numSeverity]flushSyncWriter{new(flushBuffer), new(flushBuffer), new(flushBuffer), new(flushBuffer)})
+}
+
+// contents returns the specified log value as a string.
+func contents(s severity) string {
+	return logging.file[s].(*flushBuffer).String()
+}
+
+// contains reports whether the string is contained in the log.
+func contains(s severity, str string, t *testing.T) bool {
+	return strings.Contains(contents(s), str)
+}
+
+// setFlags configures the logging flags how the test expects them.
+func setFlags() {
+	logging.toStderr = false
+}
+
+// Test that Info works as advertised.
+func TestInfo(t *testing.T) {
+	setFlags()
+	defer logging.swap(logging.newBuffers())
+	Info("test")
+	if !contains(infoLog, "I", t) {
+		t.Errorf("Info has wrong character: %q", contents(infoLog))
+	}
+	if !contains(infoLog, "test", t) {
+		t.Error("Info failed")
+	}
+}
+
+// Test that the header has the correct format.
+func TestHeader(t *testing.T) {
+	setFlags()
+	defer logging.swap(logging.newBuffers())
+	defer func(previous func() time.Time) { timeNow = previous }(timeNow)
+	timeNow = func() time.Time {
+		return time.Date(2006, 1, 2, 15, 4, 5, .678901e9, time.Local)
+	}
+	Info("test")
+	var line, pid int
+	n, err := fmt.Sscanf(contents(infoLog), "I0102 15:04:05.678901 %d glog_test.go:%d] test\n", &pid, &line)
+	if n != 2 || err != nil {
+		t.Errorf("log format error: %d elements, error %s:\n%s", n, err, contents(infoLog))
+	}
+}
+
+// Test that an Error log goes to Warning and Info.
+// Even in the Info log, the source character will be E, so the data should
+// all be identical.
+func TestError(t *testing.T) {
+	setFlags()
+	defer logging.swap(logging.newBuffers())
+	Error("test")
+	if !contains(errorLog, "E", t) {
+		t.Errorf("Error has wrong character: %q", contents(errorLog))
+	}
+	if !contains(errorLog, "test", t) {
+		t.Error("Error failed")
+	}
+	str := contents(errorLog)
+	if !contains(warningLog, str, t) {
+		t.Error("Warning failed")
+	}
+	if !contains(infoLog, str, t) {
+		t.Error("Info failed")
+	}
+}
+
+// Test that a Warning log goes to Info.
+// Even in the Info log, the source character will be W, so the data should
+// all be identical.
+func TestWarning(t *testing.T) {
+	setFlags()
+	defer logging.swap(logging.newBuffers())
+	Warning("test")
+	if !contains(warningLog, "W", t) {
+		t.Errorf("Warning has wrong character: %q", contents(warningLog))
+	}
+	if !contains(warningLog, "test", t) {
+		t.Error("Warning failed")
+	}
+	str := contents(warningLog)
+	if !contains(infoLog, str, t) {
+		t.Error("Info failed")
+	}
+}
+
+// Test that a V log goes to Info.
+func TestV(t *testing.T) {
+	setFlags()
+	defer logging.swap(logging.newBuffers())
+	logging.verbosity.Set("2")
+	defer logging.verbosity.Set("0")
+	V(2).Info("test")
+	if !contains(infoLog, "I", t) {
+		t.Errorf("Info has wrong character: %q", contents(infoLog))
+	}
+	if !contains(infoLog, "test", t) {
+		t.Error("Info failed")
+	}
+}
+
+// Test that a vmodule enables a log in this file.
+func TestVmoduleOn(t *testing.T) {
+	setFlags()
+	defer logging.swap(logging.newBuffers())
+	logging.vmodule.Set("glog_test=2")
+	defer logging.vmodule.Set("")
+	if !V(1) {
+		t.Error("V not enabled for 1")
+	}
+	if !V(2) {
+		t.Error("V not enabled for 2")
+	}
+	if V(3) {
+		t.Error("V enabled for 3")
+	}
+	V(2).Info("test")
+	if !contains(infoLog, "I", t) {
+		t.Errorf("Info has wrong character: %q", contents(infoLog))
+	}
+	if !contains(infoLog, "test", t) {
+		t.Error("Info failed")
+	}
+}
+
+// Test that a vmodule of another file does not enable a log in this file.
+func TestVmoduleOff(t *testing.T) {
+	setFlags()
+	defer logging.swap(logging.newBuffers())
+	logging.vmodule.Set("notthisfile=2")
+	defer logging.vmodule.Set("")
+	for i := 1; i <= 3; i++ {
+		if V(Level(i)) {
+			t.Errorf("V enabled for %d", i)
+		}
+	}
+	V(2).Info("test")
+	if contents(infoLog) != "" {
+		t.Error("V logged incorrectly")
+	}
+}
+
+// vGlobs are patterns that match/don't match this file at V=2.
+var vGlobs = map[string]bool{
+	// Easy to test the numeric match here.
+	"glog_test=1": false, // If -vmodule sets V to 1, V(2) will fail.
+	"glog_test=2": true,
+	"glog_test=3": true, // If -vmodule sets V to 1, V(3) will succeed.
+	// These all use 2 and check the patterns. All are true.
+	"*=2":           true,
+	"?l*=2":         true,
+	"????_*=2":      true,
+	"??[mno]?_*t=2": true,
+	// These all use 2 and check the patterns. All are false.
+	"*x=2":         false,
+	"m*=2":         false,
+	"??_*=2":       false,
+	"?[abc]?_*t=2": false,
+}
+
+// Test that vmodule globbing works as advertised.
+func testVmoduleGlob(pat string, match bool, t *testing.T) {
+	setFlags()
+	defer logging.swap(logging.newBuffers())
+	defer logging.vmodule.Set("")
+	logging.vmodule.Set(pat)
+	if V(2) != Verbose(match) {
+		t.Errorf("incorrect match for %q: got %t expected %t", pat, V(2), match)
+	}
+}
+
+// Test that a vmodule globbing works as advertised.
+func TestVmoduleGlob(t *testing.T) {
+	for glob, match := range vGlobs {
+		testVmoduleGlob(glob, match, t)
+	}
+}
+
+func TestRollover(t *testing.T) {
+	setFlags()
+	var err error
+	defer func(previous func(error)) { logExitFunc = previous }(logExitFunc)
+	logExitFunc = func(e error) {
+		err = e
+	}
+	defer func(previous uint64) { MaxSize = previous }(MaxSize)
+	MaxSize = 512
+
+	Info("x") // Be sure we have a file.
+	info, ok := logging.file[infoLog].(*syncBuffer)
+	if !ok {
+		t.Fatal("info wasn't created")
+	}
+	if err != nil {
+		t.Fatalf("info has initial error: %v", err)
+	}
+	fname0 := info.file.Name()
+	Info(strings.Repeat("x", int(MaxSize))) // force a rollover
+	if err != nil {
+		t.Fatalf("info has error after big write: %v", err)
+	}
+
+	// Make sure the next log file gets a file name with a different
+	// time stamp.
+	//
+	// TODO: determine whether we need to support subsecond log
+	// rotation.  C++ does not appear to handle this case (nor does it
+	// handle Daylight Savings Time properly).
+	time.Sleep(1 * time.Second)
+
+	Info("x") // create a new file
+	if err != nil {
+		t.Fatalf("error after rotation: %v", err)
+	}
+	fname1 := info.file.Name()
+	if fname0 == fname1 {
+		t.Errorf("info.f.Name did not change: %v", fname0)
+	}
+	if info.nbytes >= MaxSize {
+		t.Errorf("file size was not reset: %d", info.nbytes)
+	}
+}
+
+func TestLogBacktraceAt(t *testing.T) {
+	setFlags()
+	defer logging.swap(logging.newBuffers())
+	// The peculiar style of this code simplifies line counting and maintenance of the
+	// tracing block below.
+	var infoLine string
+	setTraceLocation := func(file string, line int, ok bool, delta int) {
+		if !ok {
+			t.Fatal("could not get file:line")
+		}
+		_, file = filepath.Split(file)
+		infoLine = fmt.Sprintf("%s:%d", file, line+delta)
+		err := logging.traceLocation.Set(infoLine)
+		if err != nil {
+			t.Fatal("error setting log_backtrace_at: ", err)
+		}
+	}
+	{
+		// Start of tracing block. These lines know about each other's relative position.
+		_, file, line, ok := runtime.Caller(0)
+		setTraceLocation(file, line, ok, +2) // Two lines between Caller and Info calls.
+		Info("we want a stack trace here")
+	}
+	numAppearances := strings.Count(contents(infoLog), infoLine)
+	if numAppearances < 2 {
+		// Need 2 appearances, one in the log header and one in the trace:
+		//   log_test.go:281: I0511 16:36:06.952398 02238 log_test.go:280] we want a stack trace here
+		//   ...
+		//   github.com/glog/glog_test.go:280 (0x41ba91)
+		//   ...
+		// We could be more precise but that would require knowing the details
+		// of the traceback format, which may not be dependable.
+		t.Fatal("got no trace back; log is ", contents(infoLog))
+	}
+}
+
+func BenchmarkHeader(b *testing.B) {
+	for i := 0; i < b.N; i++ {
+		logging.putBuffer(logging.header(infoLog))
+	}
+}

+ 21 - 0
build

@@ -0,0 +1,21 @@
+#!/bin/bash -e
+
+ORG_PATH="github.com/coreos-inc"
+REPO_PATH="${ORG_PATH}/kolach"
+
+if [ ! -h gopath/src/${REPO_PATH} ]; then
+	mkdir -p gopath/src/${ORG_PATH}
+	ln -s ../../../.. gopath/src/${REPO_PATH} || exit 255
+fi
+
+export GOBIN=${PWD}/bin
+export GOPATH=${PWD}/gopath
+
+eval $(go env)
+
+if [ ${GOOS} = "linux" ]; then
+	echo "Building kolach..."
+	go install ${REPO_PATH}
+else
+	echo "Not on Linux - skipping kolach build"
+fi

+ 27 - 0
cover

@@ -0,0 +1,27 @@
+#!/bin/bash -e
+#
+# Generate coverage HTML for a package
+# e.g. PKG=./unit ./cover
+#
+
+if [ -z "$PKG" ]; then
+	echo "cover only works with a single package, sorry"
+	exit 255
+fi
+
+COVEROUT="coverage"
+
+if ! [ -d "$COVEROUT" ]; then
+	mkdir "$COVEROUT"
+fi
+
+# strip out slashes and dots
+COVERPKG=${PKG//\//}
+COVERPKG=${COVERPKG//./}
+
+# generate arg for "go test"
+export COVER="-coverprofile ${COVEROUT}/${COVERPKG}.out"
+
+source ./test
+
+go tool cover -html=${COVEROUT}/${COVERPKG}.out

+ 50 - 0
test

@@ -0,0 +1,50 @@
+#!/bin/bash -e
+#
+# Run all kolach tests (not including functional)
+#   ./test
+#   ./test -v
+#
+# Run tests for one package
+#   PKG=./unit ./test
+#   PKG=ssh ./test
+#
+
+# Invoke ./cover for HTML output
+COVER=${COVER:-"-cover"}
+
+source ./build
+
+TESTABLE="pkg subnet"
+FORMATTABLE="$TESTABLE"
+
+# user has not provided PKG override
+if [ -z "$PKG" ]; then
+	TEST=$TESTABLE
+	FMT=$FORMATTABLE
+
+# user has provided PKG override
+else
+	# strip out slashes and dots from PKG=./foo/
+	TEST=${PKG//\//}
+	TEST=${TEST//./}
+
+	# only run gofmt on packages provided by user
+	FMT="$TEST"
+fi
+
+# split TEST into an array and prepend REPO_PATH to each local package
+split=(${TEST// / })
+TEST=${split[@]/#/${REPO_PATH}/}
+
+echo "Running tests..."
+go test -i ${TEST}
+go test ${COVER} $@ ${TEST}
+
+echo "Checking gofmt..."
+fmtRes=$(gofmt -l $FMT)
+if [ -n "${fmtRes}" ]; then
+	echo -e "gofmt checking failed:\n${fmtRes}"
+	exit 255
+fi
+
+echo "Success"