A Continuul extension for go-discover

go-discover

Hashicorp crafted a simple but useful library named go-discover that permits nodes in P2P systems to automatically discover each other.

It supports several cloud providers, some of interest, other minor players are of no interest to us.

Fortunately Hashicorp provided a means to define your own subset, or even extend, the list of supported providers:

// support discovery for all supported providers
d := discover.Discover{}

// support discovery for AWS and GCE only
d := discover.Discover{
    Providers: map[string]discover.Provider{
        "aws":        discover.Providers["aws"],
        "gce":        discover.Providers["gce"],
    },
}

// use ioutil.Discard for no log output
l := log.New(os.Stderr, "", log.LstdFlags)

cfg := "provider=aws region=eu-west-1 ..."
addrs, err := d.Addrs(cfg, l)

While this works great for us as we embed Serf into our own systems, it does not help with extending libraries such as Consul with additional providers without recompilation as there is no actual registration facility. Until a registration facility exists to change existing products to support platforms such as Kubernetes or Mesos, there is the above workaround.

Here at Continuul, we use Kubernetes daily, and lacking support for this platform was incompatible with our vision, so we added support.

Using a Kubernetes In-Cluster Config object, we look up the Pod IP addresses for a given Service; as nodes are added to the service using labels and selectors, the IP address list is updated automatically, and each node that joins queries this list, then joins each node automatically. The code to list the current set of nodes is simply:

    service, err := client.Core().Services(namespace).Get(name, meta_v1.GetOptions{})
    if err != nil {
        return nil, fmt.Errorf("discover-kubernetes: %s", err)
    }

    var podIpAddresses []string
    set := labels.Set(service.Spec.Selector)
    pods, err := client.Core().Pods(namespace).List(meta_v1.ListOptions{LabelSelector: set.AsSelector().String()})
    if err != nil {
        return nil, fmt.Errorf("discover-kubernetes: listing pods of service [%s]: %v", service.GetName(), err)
    }
    for _, v := range pods.Items {
        podIpAddresses = append(podIpAddresses, v.Status.PodIP)
    }

To use our library extension with the Hashicorp library is simple:

go get -u github.com/hashicorp/go-discover
go get -u continuul.io/go-discover

Then change the set of providers accordingly:

// support discovery for AWS, GCE, and Kubernetes only
d := discover.Discover{
    Providers: map[string]discover.Provider{
        "aws":        discover.Providers["aws"],
        "gce":        discover.Providers["gce"],
        "kubernetes": &kubernetes.Provider{},
    },
}

// use ioutil.Discard for no log output
l := log.New(os.Stderr, "", log.LstdFlags)

cfg := "provider=aws region=eu-west-1 ..."
addrs, err := d.Addrs(cfg, l)

Visit our go-discover project page for more information, or try it out in action with our ON Autonomic Agent within Kubernetes.