func Init(docker *goDocker.Client, newContChan chan<- goDocker.Container, removeContChan chan<- string, drawStatsChan chan<- StatsMsg) { dockerEventChan = make(chan *goDocker.APIEvents, 10) statsResultsChan = make(chan StatsResult) statsResultsDoneChan = make(chan string) dockerClient = docker err := docker.AddEventListener(dockerEventChan) if err != nil { panic("Failed to add event listener: " + err.Error()) } go statsRenderingRoutine(drawStatsChan) go dockerEventRoutingRoutine(dockerEventChan, newContChan, removeContChan) containers, _ := dockerClient.ListContainers(goDocker.ListContainersOptions{}) Info.Println("Listing intial", len(containers), "containers as started") for _, cont := range containers { Info.Println("Marking", cont.ID, "as started") dockerEventChan <- &goDocker.APIEvents{ID: cont.ID, Status: "start"} //startedContainerChan <- cont.ID } }
// RegisterDockerEventListener registers function as event listener with docker. // laziness defines how many seconds to wait, after an event is received, until a refresh is triggered func RegisterDockerEventListener(client *docker.Client, function func(), wg *sync.WaitGroup, laziness int) { wg.Add(1) defer wg.Done() events := make(chan *docker.APIEvents) defer close(events) err := client.AddEventListener((chan<- *docker.APIEvents)(events)) if err != nil { log.Fatalf("Unable to add docker event listener: %s", err) } defer client.RemoveEventListener(events) log.Info("Listening to docker events...") for { event := <-events if event == nil { continue } if event.Status == "start" || event.Status == "stop" || event.Status == "die" { log.Debug("Received event %s for container %s", event.Status, event.ID[:12]) Refresh.Mu.Lock() if !Refresh.IsTriggered { log.Info("Triggering refresh in %d seconds", laziness) Refresh.timer = time.AfterFunc(time.Duration(laziness)*time.Second, function) Refresh.IsTriggered = true } Refresh.Mu.Unlock() } } }
func main() { logger = log.New(os.Stdout, "logger: ", log.Lshortfile|log.Ldate|log.Ltime) var VERSION = os.Getenv("CCP_VERSION") logger.Println("dnsbridgeserver " + VERSION + ": starting") getEnvVars() var dockerConnected = false var tries = 0 var docker *dockerapi.Client var err error for tries = 0; tries < MAX_TRIES; tries++ { docker, err = dockerapi.NewClient(DOCKER_URL) err = docker.Ping() if err != nil { logger.Println("could not ping docker host") logger.Println("sleeping and will retry in %d sec\n", delaySeconds) time.Sleep(delay) } else { logger.Println("no err in connecting to docker") dockerConnected = true break } } if dockerConnected == false { logger.Println("failing, could not connect to docker after retries") panic("cant connect to docker") } events := make(chan *dockerapi.APIEvents) assert(docker.AddEventListener(events)) logger.Println("dnsbridgeserver: Listening for Docker events...") for msg := range events { switch msg.Status { //case "start", "create": case "start": logger.Println("event: " + msg.Status + " ID=" + msg.ID + " From:" + msg.From) dnsbridgeapi.Action(logger, msg.Status, msg.ID, docker, TTL, CONSUL_URL, DOMAIN) case "stop": logger.Println("event: " + msg.Status + " ID=" + msg.ID + " From:" + msg.From) dnsbridgeapi.Action(logger, msg.Status, msg.ID, docker, TTL, CONSUL_URL, DOMAIN) case "destroy": logger.Println("event: " + msg.Status + " ID=" + msg.ID + " From:" + msg.From) dnsbridgeapi.Action(logger, msg.Status, msg.ID, docker, TTL, CONSUL_URL, DOMAIN) case "die": logger.Println("event: " + msg.Status + " ID=" + msg.ID + " From:" + msg.From) default: logger.Println("event: " + msg.Status) } } }
func RunPool(config *Config, client *docker.Client) { chEvents := make(chan *docker.APIEvents) pools = make(map[string]*Pool) // Setup docker event listener if err := client.AddEventListener(chEvents); err != nil { log.Fatalln(err) } go func() { for { event := <-chEvents if event == nil { continue } if event.Status == "die" { for _, pool := range pools { if pool.Exists(event.ID) { log.Println("pool's container got destroyed:", event.ID) pool.Remove(event.ID) } } } } }() for _, cfg := range config.Pools { if cfg.Capacity < 1 { continue } log.Println("initializing pool for:", cfg.Image) pool, err := NewPool(config, client, cfg.Image, cfg.Capacity, cfg.Standby) if err != nil { log.Fatalln(err) } err = pool.Load() if err != nil { log.Fatalln(err) } go pool.Monitor() pools[cfg.Image] = pool } }
func generateFromEvents(client *docker.Client, configs ConfigFile) { configs = configs.filterWatches() if len(configs.Config) == 0 { return } wg.Add(1) defer wg.Done() for { if client == nil { var err error endpoint, err := getEndpoint() if err != nil { log.Printf("Bad endpoint: %s", err) time.Sleep(10 * time.Second) continue } client, err = NewDockerClient(endpoint) if err != nil { log.Printf("Unable to connect to docker daemon: %s", err) time.Sleep(10 * time.Second) continue } generateFromContainers(client) } eventChan := make(chan *docker.APIEvents, 100) defer close(eventChan) watching := false for { if client == nil { break } err := client.Ping() if err != nil { log.Printf("Unable to ping docker daemon: %s", err) if watching { client.RemoveEventListener(eventChan) watching = false client = nil } time.Sleep(10 * time.Second) break } if !watching { err = client.AddEventListener(eventChan) if err != nil && err != docker.ErrListenerAlreadyExists { log.Printf("Error registering docker event listener: %s", err) time.Sleep(10 * time.Second) continue } watching = true log.Println("Watching docker events") } select { case event := <-eventChan: if event == nil { if watching { client.RemoveEventListener(eventChan) watching = false client = nil } break } if event.Status == "start" || event.Status == "stop" || event.Status == "die" { log.Printf("Received event %s for container %s", event.Status, event.ID[:12]) generateFromContainers(client) } case <-time.After(10 * time.Second): // check for docker liveness } } } }
func registerContainers(docker *dockerapi.Client, events chan *dockerapi.APIEvents, dns resolver.Resolver, containerDomain string, hostIP net.IP) error { // TODO add an options struct instead of passing all as parameters // though passing the events channel from an options struct was triggering // data race warnings within AddEventListener, so needs more investigation if events == nil { events = make(chan *dockerapi.APIEvents) } if err := docker.AddEventListener(events); err != nil { return err } if !strings.HasPrefix(containerDomain, ".") { containerDomain = "." + containerDomain } getAddress := func(container *dockerapi.Container) (net.IP, error) { for { if container.NetworkSettings.IPAddress != "" { return net.ParseIP(container.NetworkSettings.IPAddress), nil } if container.HostConfig.NetworkMode == "host" { if hostIP == nil { return nil, errors.New("IP not available with network mode \"host\"") } else { return hostIP, nil } } if strings.HasPrefix(container.HostConfig.NetworkMode, "container:") { otherId := container.HostConfig.NetworkMode[len("container:"):] var err error container, err = docker.InspectContainer(otherId) if err != nil { return nil, err } continue } return nil, fmt.Errorf("unknown network mode", container.HostConfig.NetworkMode) } } addContainer := func(containerId string) error { container, err := docker.InspectContainer(containerId) if err != nil { return err } addr, err := getAddress(container) if err != nil { return err } err = dns.AddHost(containerId, addr, container.Config.Hostname, container.Name[1:]+containerDomain) if err != nil { return err } env := parseContainerEnv(container.Config.Env, "DNS_") if dnsDomains, ok := env["DNS_RESOLVES"]; ok { if dnsDomains == "" { return errors.New("empty DNS_RESOLVES, should contain a comma-separated list with at least one domain") } port := 53 if portString := env["DNS_PORT"]; portString != "" { port, err = strconv.Atoi(portString) if err != nil { return errors.New("invalid DNS_PORT \"" + portString + "\", should contain a number") } } domains := strings.Split(dnsDomains, ",") err = dns.AddUpstream(containerId, addr, port, domains...) if err != nil { return err } } if bridge := container.NetworkSettings.Bridge; bridge != "" { bridgeAddr := net.ParseIP(container.NetworkSettings.Gateway) err = dns.AddHost("bridge:"+bridge, bridgeAddr, bridge) if err != nil { return err } } return nil } containers, err := docker.ListContainers(dockerapi.ListContainersOptions{}) if err != nil { return err } for _, listing := range containers { if err := addContainer(listing.ID); err != nil { log.Printf("error adding container %s: %s\n", listing.ID[:12], err) } } if err = dns.Listen(); err != nil { return err } defer dns.Close() for msg := range events { go func(msg *dockerapi.APIEvents) { switch msg.Status { case "start": if err := addContainer(msg.ID); err != nil { log.Printf("error adding container %s: %s\n", msg.ID[:12], err) } case "die": dns.RemoveHost(msg.ID) dns.RemoveUpstream(msg.ID) } }(msg) } return errors.New("docker event loop closed") }