Beispiel #1
0
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)
}
Beispiel #2
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)
		}
	}
}
Beispiel #3
0
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{}),
	}
}
Beispiel #4
0
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)
	}
}
Beispiel #5
0
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
}
Beispiel #6
0
// 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()
}
Beispiel #7
0
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()
}