func TestListenerEvents(t *testing.T) { listener, st, dc := setup("10.98.90.111") // starting condition require.Len(t, allInstances(st), 0) events := make(chan *docker.APIEvents, 2) changes := make(chan data.ServiceChange, 1) dc.listenToEvents(events) st.WatchServices(nil, changes, daemon.NewErrorSink(), store.QueryServiceOptions{WithContainerRules: true}) // no services defined dc.startContainers(container{ ID: "foo", Image: "foo-image:latest", IPAddress: "192.168.0.67", }) listener.processDockerEvent(<-events) require.Len(t, allInstances(st), 0) st.AddService("foo-svc", data.Service{}) listener.processServiceChange(<-changes) addGroup(st, "foo-svc", nil, "image", "foo-image") listener.processServiceChange(<-changes) require.Len(t, allInstances(st), 1) addGroup(st, "foo-svc", nil, "image", "not-foo-image") listener.processServiceChange(<-changes) require.Len(t, allInstances(st), 0) dc.startContainers(container{ ID: "bar", IPAddress: "192.168.34.87", Image: "not-foo-image:version", }, container{ ID: "baz", IPAddress: "192.168.34.99", Image: "not-foo-image:version2", }) listener.processDockerEvent(<-events) listener.processDockerEvent(<-events) require.Len(t, allInstances(st), 2) dc.stopContainer("baz") listener.processDockerEvent(<-events) require.Len(t, allInstances(st), 1) st.RemoveService("foo-svc") listener.processServiceChange(<-changes) require.Len(t, allInstances(st), 0) }
func (l *Listener) Run(events <-chan *docker.APIEvents) { changes := make(chan data.ServiceChange) l.store.WatchServices(nil, changes, daemon.NewErrorSink(), store.QueryServiceOptions{WithContainerRules: true}) for { select { case event := <-events: l.processDockerEvent(event) case change := <-changes: l.processServiceChange(change) } } }
func newBalancerAgent(t *testing.T) *BalancerAgent { dir, err := ioutil.TempDir("", "balagent_test") require.Nil(t, err) return &BalancerAgent{ errorSink: daemon.NewErrorSink(), store: inmem.NewInMemStore(), filename: path.Join(dir, "output"), generated: make(chan struct{}), updaterStopped: make(chan struct{}), generatorStopped: make(chan struct{}), } }
func TestBalancer(t *testing.T) { ipTables := newMockIPTables(t) d := BalancerDaemon{ errorSink: daemon.NewErrorSink(), ipTablesCmd: ipTables.cmd, controller: mockController{}, eventHandler: eventlogger.EventLogger{}, netConfig: netConfig{ chain: "FLUX", bridge: "docker0", }, } d.Start() require.Empty(t, d.errorSink) d.Stop() // check that iptables was cleaned up for c, _ := range ipTables.chains { require.Contains(t, builtinChains, c) } }
func newWatcher(s store.Store, opts store.QueryServiceOptions) *watcher { w := &watcher{stopCh: make(chan struct{}), done: make(chan struct{})} changes := make(chan data.ServiceChange) ctx, cancel := context.WithCancel(context.Background()) s.WatchServices(ctx, changes, daemon.NewErrorSink(), opts) go func() { defer close(w.done) for { select { case change := <-changes: w.changes = append(w.changes, change) case <-w.stopCh: cancel() return } } }() return w }
// Test that forward.go plugs everything together correctly, and // exercise the tcp shim. func TestForward(t *testing.T) { nc := netConfig{ chain: "FLUX", bridge: "lo", } mipt := newMockIPTables(t) ipTables := newIPTables(nc, mipt.cmd) ipTables.start() listener, err := net.ListenTCP("tcp", nil) require.Nil(t, err) laddr := listener.Addr().(*net.TCPAddr) errorSink := daemon.NewErrorSink() ss, err := forwardingConfig{ netConfig: nc, ipTables: ipTables, eventHandler: events.NullHandler{}, errorSink: errorSink, }.start(&model.Service{ Name: "service", Protocol: "tcp", IP: net.ParseIP("127.42.0.1"), Port: 8888, Instances: []model.Instance{ { Name: "inst", Group: "group", IP: laddr.IP, Port: laddr.Port, }, }, }) require.Nil(t, err) require.Len(t, mipt.chains["nat FLUX"], 1) rule := mipt.chains["nat FLUX"][0] require.Regexp(t, "^-p tcp -d 127\\.42\\.0\\.1 --dport 8888 -j DNAT --to-destination 127\\.0\\.0\\.1:\\d+$", strings.Join(rule, " ")) rng := rand.New(rand.NewSource(time.Now().UnixNano())) expect := fmt.Sprint(rng.Int63()) got := "" go func() { for { conn, err := listener.AcceptTCP() if err != nil { return } b, err := ioutil.ReadAll(conn) require.Nil(t, err) require.Nil(t, conn.Close()) got = string(b) } }() faddr, err := net.ResolveTCPAddr("tcp", rule[len(rule)-1]) require.Nil(t, err) conn, err := net.DialTCP("tcp", nil, faddr) require.Nil(t, err) _, err = conn.Write([]byte(expect)) require.Nil(t, err) require.Nil(t, conn.CloseWrite()) _, err = ioutil.ReadAll(conn) require.Nil(t, err) require.Nil(t, conn.Close()) require.Equal(t, expect, got) listener.Close() ss.stop() }
func TestServices(t *testing.T) { nc := netConfig{ chain: "FLUX", bridge: "lo", } mipt := newMockIPTables(t) ipTables := newIPTables(nc, mipt.cmd) ipTables.start() errorSink := daemon.NewErrorSink() updates := make(chan model.ServiceUpdate) done := make(chan struct{}, 1) svcs := servicesConfig{ netConfig: nc, updates: updates, ipTables: ipTables, eventHandler: events.NullHandler{}, errorSink: errorSink, done: done, }.start() ip := net.ParseIP("127.42.0.1") port := 8888 // Add a service svc := model.Service{ Name: "service", Protocol: "tcp", IP: ip, Port: port, Instances: []model.Instance{ { Name: "foo", Group: "bar", IP: net.ParseIP("127.0.0.1"), Port: 10000, }, }, } updates <- model.ServiceUpdate{Service: svc} <-done requireForwarding(t, &mipt) insts := []model.Instance{ { Name: "foo", Group: "bar", IP: net.ParseIP("127.0.0.1"), Port: 10001, }, } // Update it svc.Instances = insts updates <- model.ServiceUpdate{Service: svc} <-done requireForwarding(t, &mipt) // forwarding -> rejecting svc.Instances = nil updates <- model.ServiceUpdate{Service: svc} <-done requireRejecting(t, &mipt) // rejecting -> not forwarding svc.IP = nil svc.Port = 0 updates <- model.ServiceUpdate{Service: svc} <-done requireNotForwarding(t, &mipt) // not forwarding -> forwarding svc.IP = ip svc.Port = port svc.Instances = insts updates <- model.ServiceUpdate{Service: svc} <-done requireForwarding(t, &mipt) // Now back the other way // forwarding -> not forwarding svc.IP = nil svc.Port = 0 updates <- model.ServiceUpdate{Service: svc} <-done requireNotForwarding(t, &mipt) // not forwarding -> rejecting svc.IP = ip svc.Port = port svc.Instances = nil updates <- model.ServiceUpdate{Service: svc} <-done requireRejecting(t, &mipt) // Delete it updates <- model.ServiceUpdate{Service: svc, Delete: true} <-done requireNotForwarding(t, &mipt) // Delete it, even though it doesn't exist updates <- model.ServiceUpdate{Service: svc, Delete: true} <-done svcs.close() }