diff --git a/clab/config.go b/clab/config.go index bdb17fd28..fb1ae7efd 100644 --- a/clab/config.go +++ b/clab/config.go @@ -21,7 +21,7 @@ const ( dockerNetIPv4Addr = "172.20.20.0/24" dockerNetIPv6Addr = "2001:172:20:20::/80" dockerNetMTU = "1450" - srlDefaultType = "ixr6" + srlDefaultType = "ixr6" ) // supported kinds @@ -119,12 +119,14 @@ type Node struct { TLSCert string TLSKey string TLSAnchor string + NSPath string // network namespace path for this node } // Link is a struct that contains the information of a link between 2 containers type Link struct { A *Endpoint B *Endpoint + MTU int Labels map[string]string } @@ -470,6 +472,10 @@ func (c *cLab) NewLink(l LinkConfig) *Link { link := new(Link) link.Labels = l.Labels + if link.MTU <= 0 { + link.MTU = 1500 + } + for i, d := range l.Endpoints { // i indicates the number and d presents the string, which need to be // split in node and endpoint name diff --git a/clab/docker.go b/clab/docker.go index ec66445cc..b24aa2d8c 100644 --- a/clab/docker.go +++ b/clab/docker.go @@ -23,8 +23,7 @@ const sysctlBase = "/proc/sys" // CreateBridge creates a docker bridge func (c *cLab) CreateBridge(ctx context.Context) (err error) { - log.Info("Creating docker bridge") - log.Debugf("Creating docker bridge: Name: %s, IPv4Subnet: '%s', IPv6Subnet: '%s", c.Config.Mgmt.Network, c.Config.Mgmt.IPv4Subnet, c.Config.Mgmt.IPv6Subnet) + log.Infof("Creating docker network: Name='%s', IPv4Subnet='%s', IPv6Subnet='%s'", c.Config.Mgmt.Network, c.Config.Mgmt.IPv4Subnet, c.Config.Mgmt.IPv6Subnet) enableIPv6 := false var ipamConfig []network.IPAMConfig @@ -144,7 +143,7 @@ func (c *cLab) DeleteBridge(ctx context.Context) (err error) { // CreateContainer creates a docker container func (c *cLab) CreateContainer(ctx context.Context, node *Node) (err error) { - log.Infof("Create container: %s", node.ShortName) + log.Infof("Creating container: %s", node.ShortName) err = c.PullImageIfRequired(ctx, node.Image) if err != nil { @@ -212,7 +211,8 @@ func (c *cLab) CreateContainer(ctx context.Context, node *Node) (err error) { if err != nil { return err } - return linkContainerNS(cJSON.State.Pid, node.LongName) + node.NSPath = "/proc/" + strconv.Itoa(cJSON.State.Pid) + "/ns/net" + return linkContainerNS(node.NSPath, node.LongName) } func (c *cLab) PullImageIfRequired(ctx context.Context, imageName string) error { @@ -345,13 +345,14 @@ func (c *cLab) DeleteContainer(ctx context.Context, name string) error { var err error force := !c.gracefulShutdown if c.gracefulShutdown { + log.Infof("Stopping container: %s", name) err = c.DockerClient.ContainerStop(ctx, name, &c.timeout) if err != nil { log.Errorf("could not stop container '%s': %v", name, err) force = true } } - log.Infof("Removing container: %s", name) + log.Debugf("Removing container: %s", name) err = c.DockerClient.ContainerRemove(ctx, name, types.ContainerRemoveOptions{Force: force}) if err != nil { return err @@ -362,11 +363,10 @@ func (c *cLab) DeleteContainer(ctx context.Context, name string) error { // linkContainerNS creates a symlink for containers network namespace // so that it can be managed by iproute2 utility -func linkContainerNS(pid int, containerName string) error { +func linkContainerNS(nspath string, containerName string) error { CreateDirectory("/run/netns/", 0755) - src := "/proc/" + strconv.Itoa(pid) + "/ns/net" dst := "/run/netns/" + containerName - err := os.Symlink(src, dst) + err := os.Symlink(nspath, dst) if err != nil { return err } diff --git a/clab/file.go b/clab/file.go index 6876794b1..2da646b43 100644 --- a/clab/file.go +++ b/clab/file.go @@ -144,7 +144,7 @@ func (c *cLab) CreateNodeDirStructure(node *Node) (err error) { switch node.Kind { case "srl": - log.Infof("Create directory structure for SRL container: %s", node.ShortName) + log.Debugf("Creating directory structure for SRL container: %s", node.ShortName) var src string var dst string diff --git a/clab/netlink.go b/clab/netlink.go index ac726126c..54d09c7d1 100644 --- a/clab/netlink.go +++ b/clab/netlink.go @@ -4,161 +4,171 @@ import ( "fmt" "net" "os" - "os/exec" - "strings" - "sync" + "github.com/containernetworking/plugins/pkg/ns" "github.com/google/uuid" log "github.com/sirupsen/logrus" "github.com/vishvananda/netlink" ) -func (c *cLab) InitVirtualWiring() { - // list interfaces - log.Debug("listing system interfaces...") - interfaces, err := net.Interfaces() - if err != nil { - log.Warnf("failed to get system interfaces:%v", err) - return - } - log.Debugf("found %d interfaces", len(interfaces)) - for i := range interfaces { - if strings.HasPrefix(interfaces[i].Name, "clab-") { - log.Debugf("deleting interface %s", interfaces[i].Name) - l, err := netlink.LinkByName(interfaces[i].Name) - if err != nil { - log.Debugf("failed to find interface for deletion by name: %v", interfaces[i].Name) - continue - } - err = netlink.LinkDel(l) - if err != nil { - log.Debugf("failed to delete interface %s: %v", interfaces[i].Name, err) - } - } - } +type vEthEndpoint struct { + Link netlink.Link + LinkName string + NSName string // netns name + NSPath string // netns path + Bridge string // bridge name a veth is destined to be connected to } // CreateVirtualWiring provides the virtual topology between the containers -func (c *cLab) CreateVirtualWiring(link *Link) (err error) { - log.Infof("Create virtual wire : %s:%s <--> %s:%s", link.A.Node.LongName, link.A.EndpointName, link.B.Node.LongName, link.B.EndpointName) - if link.A.Node.Kind != "bridge" && link.B.Node.Kind != "bridge" { - return c.createAToBveth(link) - } - return c.createvethToBridge(link) -} - -func (c *cLab) createAToBveth(l *Link) error { - interfaceA := fmt.Sprintf("clab-%s", genIfName()) - interfaceB := fmt.Sprintf("clab-%s", genIfName()) - - cmd := exec.Command("sudo", "ip", "link", "add", interfaceA, "type", "veth", "peer", "name", interfaceB) - err := runCmd(cmd) +func (c *cLab) CreateVirtualWiring(l *Link) (err error) { + log.Infof("Creating virtual wire: %s:%s <--> %s:%s", l.A.Node.ShortName, l.A.EndpointName, l.B.Node.ShortName, l.B.EndpointName) + + // connect containers (or container and a bridge) using veth pair + // based on the link configuration contained within *Link struct + // veth side A + vA := vEthEndpoint{ + LinkName: l.A.EndpointName, + NSName: l.A.Node.LongName, + NSPath: l.A.Node.NSPath, + } + // veth side B + vB := vEthEndpoint{ + LinkName: l.B.EndpointName, + NSName: l.B.Node.LongName, + NSPath: l.B.Node.NSPath, + } + + // get random names for veth sides as they will be created in root netns first + ARndmName := fmt.Sprintf("clab-%s", genIfName()) + BRndmName := fmt.Sprintf("clab-%s", genIfName()) + + // set bridge name for endpoint that should be connect to linux bridge + switch { + case l.A.Node.Kind == "bridge": + vA.Bridge = l.A.Node.ShortName + // veth endpoint destined to connect to the bridge in the host netns + // will not have a random name + ARndmName = l.A.EndpointName + case l.B.Node.Kind == "bridge": + vB.Bridge = l.B.Node.ShortName + BRndmName = l.B.EndpointName + } + + // create veth pair in the root netns + vA.Link, vB.Link, err = createVethIface(ARndmName, BRndmName, l.MTU) if err != nil { return err } - wg := new(sync.WaitGroup) - wg.Add(2) - go func() { - defer wg.Done() - err := c.configVeth(interfaceA, l.A.EndpointName, l.A.Node.LongName) - if err != nil { - log.Fatalf("failed to config interface '%s' in container %s: %v", l.A.EndpointName, l.A.Node.LongName, err) - } - }() - go func() { - defer wg.Done() - err = c.configVeth(interfaceB, l.B.EndpointName, l.B.Node.LongName) - if err != nil { - log.Fatalf("failed to config interface '%s' in container %s: %v", l.B.EndpointName, l.B.Node.LongName, err) - } - }() - wg.Wait() - return nil -} -func (c *cLab) configVeth(dummyInterface, endpointName, ns string) error { - var cmd *exec.Cmd - log.Debugf("Disabling TX checksum offloading for the %s interface...", dummyInterface) - err := EthtoolTXOff(dummyInterface) - if err != nil { + // once veth pair is created, disable tx offload for veth pair + if err := EthtoolTXOff(ARndmName); err != nil { return err } - log.Debugf("map dummy interface '%s' to container %s", dummyInterface, ns) - cmd = exec.Command("sudo", "ip", "link", "set", dummyInterface, "netns", ns) - err = runCmd(cmd) - if err != nil { + if err := EthtoolTXOff(BRndmName); err != nil { return err } - log.Debugf("rename interface %s to %s", dummyInterface, endpointName) - cmd = exec.Command("sudo", "ip", "netns", "exec", ns, "ip", "link", "set", dummyInterface, "name", endpointName) - err = runCmd(cmd) - if err != nil { + + if err = vA.setVethLink(); err != nil { + netlink.LinkDel(vA.Link) return err } - log.Debugf("set interface %s state to up in NS %s", endpointName, ns) - cmd = exec.Command("sudo", "ip", "netns", "exec", ns, "ip", "link", "set", endpointName, "up") - err = runCmd(cmd) - if err != nil { - return err + if err = vB.setVethLink(); err != nil { + netlink.LinkDel(vB.Link) } - return nil -} - -func (c *cLab) createvethToBridge(l *Link) error { - var cmd *exec.Cmd - var err error - log.Debugf("Create veth to bridge wire: %s <--> %s", l.A.EndpointName, l.B.EndpointName) - dummyIface := fmt.Sprintf("clab-%s", genIfName()) - // assume A is a bridge - bridgeName := l.A.Node.ShortName - bridgeIfname := l.A.EndpointName + return err - containerIfName := l.B.EndpointName - containerNS := l.B.Node.LongName +} - if l.A.Node.Kind != "bridge" { // change var values if A is not a bridge - bridgeName = l.B.Node.ShortName - bridgeIfname = l.B.EndpointName +// createVethIface takes two veth endpoint structs and create a veth pair and return +// veth interface links. +func createVethIface(ifName, peerName string, mtu int) (linkA netlink.Link, linkB netlink.Link, err error) { + linkA = &netlink.Veth{ + LinkAttrs: netlink.LinkAttrs{ + Name: ifName, + Flags: net.FlagUp, + MTU: mtu, + }, + PeerName: peerName, + } - containerIfName = l.A.EndpointName - containerNS = l.A.Node.LongName + if err := netlink.LinkAdd(linkA); err != nil { + return nil, nil, err } - log.Debugf("create dummy veth pair '%s'<-->'%s'", dummyIface, bridgeIfname) - cmd = exec.Command("sudo", "ip", "link", "add", dummyIface, "type", "veth", "peer", "name", bridgeIfname) - err = runCmd(cmd) - if err != nil { - return err + if linkB, err = netlink.LinkByName(peerName); err != nil { + err = fmt.Errorf("failed to lookup %q: %v", peerName, err) } - err = c.configVeth(dummyIface, containerIfName, containerNS) - if err != nil { - return err + + return +} + +// setVethLink sets the veth link endpoints to the relevant namespaces and/or connects one end to the bridge +func (veth *vEthEndpoint) setVethLink() error { + // if veth is destined to connect to a linux bridge in the host netns + if veth.Bridge != "" { + return veth.toBridge() } - log.Debugf("map veth pair %s to bridge %s", bridgeIfname, bridgeName) - cmd = exec.Command("sudo", "ip", "link", "set", bridgeIfname, "master", bridgeName) - err = runCmd(cmd) - if err != nil { + // otherwise it needs to be put into a netns + return veth.toNS() +} + +// vethToNS puts a veth endpoint to a given netns and renames its random name to a desired name +func (veth *vEthEndpoint) toNS() error { + var vethNS ns.NetNS + var err error + if vethNS, err = ns.GetNS(veth.NSPath); err != nil { return err } - log.Debugf("set interface '%s' state to up", bridgeIfname) - cmd = exec.Command("sudo", "ip", "link", "set", bridgeIfname, "up") - err = runCmd(cmd) - if err != nil { + // move veth endpoint to namespace + if err = netlink.LinkSetNsFd(veth.Link, int(vethNS.Fd())); err != nil { return err } - log.Debugf("Disabling TX checksum offloading for the %s interface...", bridgeIfname) - err = EthtoolTXOff(bridgeIfname) - if err != nil { + err = vethNS.Do(func(_ ns.NetNS) error { + if err = netlink.LinkSetName(veth.Link, veth.LinkName); err != nil { + return fmt.Errorf( + "failed to rename link: %v", err) + } + + if err = netlink.LinkSetUp(veth.Link); err != nil { + return fmt.Errorf("failed to set %q up: %v", + veth.LinkName, err) + } + return nil + }) + return err +} + +func (veth *vEthEndpoint) toBridge() error { + var vethNS ns.NetNS + var err error + // bride is in the host netns, thus we need to get current netns + if vethNS, err = ns.GetCurrentNS(); err != nil { return err } - return nil + err = vethNS.Do(func(_ ns.NetNS) error { + br, err := bridgeByName(veth.Bridge) + if err != nil { + return err + } + + // connect host veth end to the bridge + if err := netlink.LinkSetMaster(veth.Link, br); err != nil { + return fmt.Errorf("failed to connect %q to bridge %v: %v", veth.LinkName, veth.Bridge, err) + } + + if err = netlink.LinkSetUp(veth.Link); err != nil { + return fmt.Errorf("failed to set %q up: %v", veth.LinkName, err) + } + return nil + }) + return err } // DeleteNetnsSymlinks deletes the symlink file created for each container netns func (c *cLab) DeleteNetnsSymlinks() (err error) { for _, node := range c.Nodes { if node.Kind != "bridge" { - log.Infof("Deleting %s network namespace", node.LongName) + log.Debugf("Deleting %s network namespace", node.LongName) if err := deleteNetnsSymlink(node.LongName); err != nil { return err } @@ -169,17 +179,6 @@ func (c *cLab) DeleteNetnsSymlinks() (err error) { return nil } -func runCmd(cmd *exec.Cmd) error { - b, err := cmd.CombinedOutput() - if err != nil { - log.Debugf("'%s' failed with: %v", cmd.String(), err) - log.Debugf("'%s' failed output: %v", cmd.String(), string(b)) - return err - } - log.Debugf("'%s' output: %v", cmd.String(), string(b)) - return nil -} - func genIfName() string { s, _ := uuid.New().MarshalText() // .MarshalText() always return a nil error return string(s[:8]) @@ -195,3 +194,15 @@ func deleteNetnsSymlink(n string) error { } return nil } + +func bridgeByName(name string) (*netlink.Bridge, error) { + l, err := netlink.LinkByName(name) + if err != nil { + return nil, fmt.Errorf("could not lookup %q: %v", name, err) + } + br, ok := l.(*netlink.Bridge) + if !ok { + return nil, fmt.Errorf("%q already exists but is not a bridge", name) + } + return br, nil +} diff --git a/cmd/deploy.go b/cmd/deploy.go index 2e55eb0e5..22dc267bb 100644 --- a/cmd/deploy.go +++ b/cmd/deploy.go @@ -168,9 +168,6 @@ var deployCmd = &cobra.Command{ // wait for all workers to finish wg.Wait() - // cleanup hanging resources if a deployment failed before - log.Debug("cleaning up interfaces...") - c.InitVirtualWiring() wg = new(sync.WaitGroup) wg.Add(int(linksMaxWorkers)) linksChan := make(chan *clab.Link) @@ -187,7 +184,7 @@ var deployCmd = &cobra.Command{ return } log.Debugf("Worker %d received link: %+v", i, link) - if err = c.CreateVirtualWiring(link); err != nil { + if err := c.CreateVirtualWiring(link); err != nil { log.Error(err) } case <-ctx.Done(): diff --git a/cmd/destroy.go b/cmd/destroy.go index c0e70a552..4fc9edc29 100644 --- a/cmd/destroy.go +++ b/cmd/destroy.go @@ -66,7 +66,6 @@ var destroyCmd = &cobra.Command{ if len(cont.Names) > 0 { name = strings.TrimLeft(cont.Names[0], "/") } - log.Infof("Stopping container: %s", name) err := c.DeleteContainer(ctx, name) if err != nil { log.Errorf("could not remove container '%s': %v", name, err) @@ -80,14 +79,13 @@ var destroyCmd = &cobra.Command{ return err } - // delete container management bridge - log.Info("Deleting docker bridge ...") + // delete lab management network + log.Infof("Deleting docker network '%s'...", c.Config.Mgmt.Network) if err = c.DeleteBridge(ctx); err != nil { log.Error(err) } // delete container network namespaces symlinks c.DeleteNetnsSymlinks() - c.InitVirtualWiring() return nil }, } diff --git a/go.mod b/go.mod index 0819635a7..c1e4d8567 100644 --- a/go.mod +++ b/go.mod @@ -6,19 +6,24 @@ require ( github.com/Microsoft/go-winio v0.4.14 // indirect github.com/awalterschulze/gographviz v2.0.1+incompatible github.com/cloudflare/cfssl v1.4.1 + github.com/containernetworking/plugins v0.0.0-20180803153142-19f2f28178aa github.com/docker/distribution v2.7.1+incompatible // indirect github.com/docker/docker v1.13.1 github.com/docker/go-connections v0.4.0 github.com/docker/go-units v0.4.0 // indirect - github.com/google/go-cmp v0.2.0 + github.com/golang/protobuf v1.3.2 // indirect + github.com/google/go-cmp v0.3.1 github.com/google/uuid v1.1.2 github.com/mitchellh/go-homedir v1.1.0 github.com/olekukonko/tablewriter v0.0.4 + github.com/onsi/ginkgo v1.10.2 // indirect + github.com/onsi/gomega v1.7.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/sirupsen/logrus v1.6.0 github.com/spf13/cobra v1.0.0 github.com/spf13/pflag v1.0.5 // indirect + github.com/stretchr/testify v1.4.0 // indirect github.com/vishvananda/netlink v1.1.0 github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae // indirect golang.org/x/net v0.0.0-20200822124328-c89045814202 // indirect diff --git a/go.sum b/go.sum index b77bb2353..a4b94a0a4 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,6 @@ bitbucket.org/liamstask/goose v0.0.0-20150115234039-8488cc47d90c/go.mod h1:hSVuE3qU7grINVSwrmzHfpg9k87ALBk+XaualNyUzI4= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/GeertJohan/go.incremental v1.0.0/go.mod h1:6fAjUhbVuX1KcMD3c8TEgVUqmo4seqhv0i0kdATSkM0= github.com/GeertJohan/go.rice v1.0.0/go.mod h1:eH6gbSOAUv07dQuZVnBmoDP8mgsM1rtixis4Tib9if0= @@ -22,6 +23,8 @@ github.com/cloudflare/cfssl v1.4.1 h1:vScfU2DrIUI9VPHBVeeAQ0q5A+9yshO1Gz+3QoUQiK github.com/cloudflare/cfssl v1.4.1/go.mod h1:KManx/OJPb5QY+y0+o/898AMcM128sF0bURvoVUSjTo= github.com/cloudflare/go-metrics v0.0.0-20151117154305-6a9aea36fb41/go.mod h1:eaZPlJWD+G9wseg1BuRXlHnjntPMrywMsyxf+LTOdP4= github.com/cloudflare/redoctober v0.0.0-20171127175943-746a508df14c/go.mod h1:6Se34jNoqrd8bTxrmJB2Bg2aoZ2CdSXonils9NsiNgo= +github.com/containernetworking/plugins v0.0.0-20180803153142-19f2f28178aa h1:9H4VmKyDPYBwwR4zI0y9HVDr8Q6Z0phICWBFw1h4810= +github.com/containernetworking/plugins v0.0.0-20180803153142-19f2f28178aa/go.mod h1:dagHaAhNjXjT9QYOklkKJDGaQPTg4pf//FrUcJeb7FU= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= @@ -42,6 +45,7 @@ github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKoh github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/getsentry/raven-go v0.0.0-20180121060056-563b81fc02b7/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= @@ -53,17 +57,22 @@ github.com/go-sql-driver/mysql v1.3.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/certificate-transparency-go v1.0.21 h1:Yf1aXowfZ2nuboBsg7iYGLmwsOARdV86pfH3g95wXmE= github.com/google/certificate-transparency-go v1.0.21/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg= github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= @@ -71,6 +80,8 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmg github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= @@ -111,6 +122,11 @@ github.com/nkovacs/streamquote v0.0.0-20170412213628-49af9bddb229/go.mod h1:0aYX github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.4 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn+Ejf/w8= github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.2 h1:uqH7bpe+ERSiDa34FDOF7RikN6RzXgduUF8yarlZp94= +github.com/onsi/ginkgo v1.10.2/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME= +github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= @@ -156,6 +172,8 @@ github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= @@ -191,6 +209,7 @@ golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTk golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -204,6 +223,7 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -221,6 +241,7 @@ golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd h1:/e+gpKk9r3dJobndpTytxS2gOy6m5uvpg+ISQoEcusQ= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= @@ -230,10 +251,15 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099 h1:XJP7lxbSxWLOMNdBE4B/STaqVy6L73o0knwj2vIlxnw= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=