package app import ( "fmt" "net" "strings" fsouza "github.com/fsouza/go-dockerclient" "github.com/weaveworks/common/backoff" ) // Default values for weave app integration const ( DefaultHostname = "scope.weave.local." DefaultWeaveURL = "http://127.0.0.1:6784" DefaultContainerName = "weavescope" ) // WeavePublisher is a thing which periodically registers this app with WeaveDNS. type WeavePublisher struct { containerName string hostname string dockerClient DockerClient weaveClient WeaveClient backoff backoff.Interface interfaces InterfaceFunc } // DockerClient is the little bit of the docker client we need. type DockerClient interface { ListContainers(fsouza.ListContainersOptions) ([]fsouza.APIContainers, error) } // WeaveClient is the little bit of the weave clent we need. type WeaveClient interface { AddDNSEntry(hostname, containerid string, ip net.IP) error Expose() error } // Interface is because net.Interface isn't mockable. type Interface struct { Name string Addrs []net.Addr } // InterfaceFunc is the type of Interfaces() type InterfaceFunc func() ([]Interface, error) // Interfaces returns the list of Interfaces on the machine. func Interfaces() ([]Interface, error) { ifaces, err := net.Interfaces() if err != nil { return nil, err } result := []Interface{} for _, i := range ifaces { addrs, err := i.Addrs() if err != nil { continue } result = append(result, Interface{ Name: i.Name, Addrs: addrs, }) } return result, nil } // NewWeavePublisher makes a new Weave. func NewWeavePublisher(weaveClient WeaveClient, dockerClient DockerClient, interfaces InterfaceFunc, hostname, containerName string) *WeavePublisher { w := &WeavePublisher{ containerName: containerName, hostname: hostname, dockerClient: dockerClient, weaveClient: weaveClient, interfaces: interfaces, } w.backoff = backoff.New(w.updateDNS, "updating weaveDNS") go w.backoff.Start() return w } // Stop the Weave. func (w *WeavePublisher) Stop() { w.backoff.Stop() } func (w *WeavePublisher) updateDNS() (bool, error) { // 0. expose this host if err := w.weaveClient.Expose(); err != nil { return false, err } // 1. work out my IP addresses ifaces, err := w.interfaces() if err != nil { return false, err } ips := []net.IP{} for _, i := range ifaces { if strings.HasPrefix(i.Name, "lo") || strings.HasPrefix(i.Name, "docker") || strings.HasPrefix(i.Name, "veth") { continue } for _, addr := range i.Addrs { var ip net.IP switch v := addr.(type) { case *net.IPAddr: ip = v.IP case *net.IPNet: ip = v.IP } if ip != nil && ip.To4() != nil { ips = append(ips, ip) } } } // 2. work out my container name containers, err := w.dockerClient.ListContainers(fsouza.ListContainersOptions{}) if err != nil { return false, err } containerID := "" outer: for _, container := range containers { for _, name := range container.Names { if name == "/"+w.containerName { containerID = container.ID break outer } } } if containerID == "" { return false, fmt.Errorf("Container %s not found", w.containerName) } // 3. Register these with weave dns for _, ip := range ips { if err := w.weaveClient.AddDNSEntry(w.hostname, containerID, ip); err != nil { return false, err } } return false, nil }