Example #1
0
func TestMachineGroupFreshStart(t *testing.T) {
	st, stop, err := testBoltStorage()
	if err != nil {
		t.Fatalf("want err = nil; got %v", err)
	}
	defer stop()

	builder := testutil.NewBuilder(nil)
	g, err := New(testOptionsStorage(builder, st))
	if err != nil {
		t.Fatalf("want err = nil; got %v", err)
	}
	defer g.Close()

	// Nothing should be added to addresses storage.
	address, err := addresses.NewCached(st)
	if err != nil {
		t.Fatalf("want err = nil; got %v", err)
	}
	if len(address.Registered()) != 0 {
		t.Errorf("want no registered machines; got %v", address.Registered())
	}

	// Nothing should be added to aliases storage.
	alias, err := aliases.NewCached(st)
	if err != nil {
		t.Fatalf("want err = nil; got %v", err)
	}
	if len(alias.Registered()) != 0 {
		t.Errorf("want no registered machines; got %v", alias.Registered())
	}
}
Example #2
0
func TestSupervisedWait(t *testing.T) {
	serv := &testutil.Server{}

	dc, err := client.NewDynamic(testutil.DynamicOpts(serv, testutil.NewBuilder(nil)))
	if err != nil {
		t.Fatalf("want err = nil; got %v", err)
	}
	defer dc.Close()

	dcf := func() (client.Client, error) { return dc.Client(), nil }

	// Server is off, so the timeout should be reached.
	const timeout = 50 * time.Millisecond
	spv := client.NewSupervised(dcf, timeout)
	startC, hitC := make(chan struct{}), make(chan time.Time)

	go func() {
		<-startC
		_, err = spv.CurrentUser()
		hitC <- time.Now()
	}()

	now := time.Now()
	close(startC)
	select {
	case called := <-hitC:
		if tret := called.Sub(now); tret < timeout {
			t.Fatalf("want return at least after %v; got %v", timeout, tret)
		}
		if err != client.ErrDisconnected {
			t.Fatalf("want err = %v; got %v", client.ErrDisconnected, err)
		}
	case <-time.After(time.Second):
		t.Fatalf("test timed out after 1s")
	}

	// Server starts to be responsive while Supervisor waits for it.
	spv = client.NewSupervised(dcf, 2*time.Second)
	startC, hitC = make(chan struct{}), make(chan time.Time)

	go func() {
		<-startC
		_, err = spv.CurrentUser()
		hitC <- time.Now()
	}()

	close(startC)
	serv.TurnOn()
	select {
	case <-hitC:
		if err == client.ErrDisconnected {
			t.Fatal("want err != client.ErrDisconnected")
		}
	case <-time.After(time.Second):
		t.Fatalf("test timed out after 1s")
	}
}
Example #3
0
func TestClients(t *testing.T) {
	var (
		builder = testutil.NewBuilder(nil)

		idA = machine.ID("servA")
		idB = machine.ID("servB")

		servA = &testutil.Server{}
		servB = &testutil.Server{}
	)

	cs, err := clients.New(testOptions(builder))
	if err != nil {
		t.Fatalf("want err = nil; got %v", err)
	}
	defer cs.Close()

	var g errgroup.Group
	create := map[machine.ID]client.DynamicAddrFunc{
		idA: servA.AddrFunc(),
		idB: servB.AddrFunc(),
		idA: servA.AddrFunc(), // duplicate.
		idA: servA.AddrFunc(), // duplicate.
	}

	for id, dynAddr := range create {
		id, dynAddr := id, dynAddr // Local copy for concurrency.
		g.Go(func() error {
			return cs.Create(id, dynAddr)
		})
	}
	if err := g.Wait(); err != nil {
		t.Fatalf("want err = nil; got %v", err)
	}

	if regs := cs.Registered(); len(regs) != 2 {
		t.Fatalf("want clients count = 2; got %d", len(regs))
	}

	if _, err := cs.Client(idA); err != nil {
		t.Fatalf("want err = nil; got %v", err)
	}

	if err := cs.Drop(idA); err != nil {
		t.Fatalf("want err = nil; got %v", err)
	}

	if _, err := cs.Client(idA); err != machine.ErrMachineNotFound {
		t.Fatalf("want machine not found; got %v", err)
	}

	if regs := cs.Registered(); len(regs) != 1 {
		t.Fatalf("want clients count = 1; got %d", len(regs))
	}
}
Example #4
0
func TestDynamicClientContext(t *testing.T) {
	var (
		serv    = &testutil.Server{}
		builder = testutil.NewBuilder(nil)
	)

	dc, err := client.NewDynamic(testutil.DynamicOpts(serv, builder))
	if err != nil {
		t.Fatalf("want err = nil; got %v", err)
	}
	defer dc.Close()

	ctx := dc.Client().Context()
	serv.TurnOn()
	if err := testutil.WaitForContextClose(ctx, time.Second); err != nil {
		t.Fatalf("want err = nil; got %v", err)
	}

	const ContextWorkers = 10

	var g errgroup.Group
	for i := 0; i < ContextWorkers; i++ {
		g.Go(func() error {
			select {
			case <-dc.Client().Context().Done():
				return errors.New("context closed unexpectedly")
			case <-time.After(50 * time.Millisecond):
				return nil
			}
		})
	}
	// Machine is on so dynamic client should not close its context.
	if err := g.Wait(); err != nil {
		t.Fatalf("want err = nil; got %v", err)
	}

	ctx = dc.Client().Context()
	serv.TurnOff()
	for i := 0; i < ContextWorkers; i++ {
		g.Go(func() error {
			select {
			case <-ctx.Done():
				return nil
			case <-time.After(time.Second):
				return errors.New("timed out")
			}
		})
	}
	// Machine is off so its context channel should be closed by dynamic client.
	if err := g.Wait(); err != nil {
		t.Fatalf("want err = nil; got %v", err)
	}
}
Example #5
0
func TestDynamicClientOnOff(t *testing.T) {
	var (
		serv    = &testutil.Server{}
		builder = testutil.NewBuilder(nil)
	)

	dc, err := client.NewDynamic(testutil.DynamicOpts(serv, builder))
	if err != nil {
		t.Fatalf("want err = nil; got %v", err)
	}
	defer dc.Close()

	// Server is in unknown state.
	if status := dc.Status(); status.State != machine.StateUnknown {
		t.Fatalf("want state = %s; got %s", machine.StateUnknown, status.State)
	}

	// Server starts responding.
	ctx := dc.Client().Context()
	serv.TurnOn()
	if err := builder.WaitForBuild(time.Second); err != nil {
		t.Fatalf("want err = nil; got %v", err)
	}
	if n := builder.BuildsCount(); n != 1 {
		t.Fatalf("want builds count = 1; got %d", n)
	}
	if err := testutil.WaitForContextClose(ctx, time.Second); err != nil {
		t.Fatalf("want err = nil; got %v", err)
	}
	if status := dc.Status(); status.State != machine.StateOnline {
		t.Fatalf("want state = %s; got %s", machine.StateOnline, status.State)
	}

	// Stop server.
	ctx = dc.Client().Context()
	serv.TurnOff()
	if err := builder.WaitForBuild(time.Second); err != nil {
		t.Fatalf("want err = nil; got %v", err)
	}
	if n := builder.BuildsCount(); n != 2 {
		t.Fatalf("want builds count = 2; got %d", n)
	}
	if err := testutil.WaitForContextClose(ctx, time.Second); err != nil {
		t.Fatalf("want err = nil; got %v", err)
	}
	if status := dc.Status(); status.State != machine.StateOffline {
		t.Fatalf("want state = %s; got %s", machine.StateOffline, status.State)
	}
}
Example #6
0
func TestMachineGroupNoAliases(t *testing.T) {
	st, stop, err := testBoltStorage()
	if err != nil {
		t.Fatalf("want err = nil; got %v", err)
	}
	defer stop()

	// Add initial address.
	id := machine.ID("servA")
	address, err := addresses.NewCached(st)
	if err != nil {
		t.Fatalf("want err = nil; got %v", err)
	}
	if err := address.Add(id, testutil.TurnOnAddr()); err != nil {
		t.Fatalf("want err = nil; got %v", err)
	}
	if len(address.Registered()) != 1 {
		t.Errorf("want one registered machine; got %v", address.Registered())
	}

	builder := testutil.NewBuilder(nil)
	g, err := New(testOptionsStorage(builder, st))
	if err != nil {
		t.Fatalf("want err = nil; got %v", err)
	}
	defer g.Close()

	// Machine group should add alias for missing ID.
	alias, err := aliases.NewCached(st)
	if err != nil {
		t.Fatalf("want err = nil; got %v", err)
	}
	if len(alias.Registered()) != 1 {
		t.Errorf("want one registered machine; got %v", address.Registered())
	}

	// Dynamic client should be started.
	if err := builder.WaitForBuild(time.Second); err != nil {
		t.Fatalf("want err = nil; got %v", err)
	}
	if builder.BuildsCount() != 1 {
		t.Errorf("want dynamic builds number = 1; got %d", builder.BuildsCount())
	}
}
Example #7
0
func TestCreateBalance(t *testing.T) {
	var (
		client  = testutil.NewClient()
		builder = testutil.NewBuilder(client)
		id      = machine.ID("serv")
	)

	g, err := New(testOptions(builder))
	if err != nil {
		t.Fatalf("want err = nil; got %v", err)
	}
	defer g.Close()

	req := &CreateRequest{
		Addresses: map[machine.ID][]machine.Addr{
			id: {testutil.TurnOffAddr()},
		},
	}

	if _, err := g.Create(req); err != nil {
		t.Fatalf("want err = nil; got %v", err)
	}

	if err := builder.WaitForBuild(time.Second); err != nil {
		t.Fatalf("want err = nil; got %v", err)
	}

	// Create with empty addresses should remove previously added machine.
	if _, err := g.Create(&CreateRequest{}); err != nil {
		t.Fatalf("want err = nil; got %v", err)
	}

	// Client context should be closed.
	if err := testutil.WaitForContextClose(client.Context(), time.Second); err != nil {
		t.Fatalf("want err = nil; got %v", err)
	}
}
Example #8
0
func TestCreate(t *testing.T) {
	var (
		builder = testutil.NewBuilder(nil)

		idA = machine.ID("servA")
		idB = machine.ID("servB")
	)

	g, err := New(testOptions(builder))
	if err != nil {
		t.Fatalf("want err = nil; got %v", err)
	}
	defer g.Close()

	const AddedServersCount = 2
	req := &CreateRequest{
		Addresses: map[machine.ID][]machine.Addr{
			idA: {testutil.TurnOffAddr()},
			idB: {testutil.TurnOnAddr()},
		},
	}

	res, err := g.Create(req)
	if err != nil {
		t.Fatalf("want err = nil; got %v", err)
	}

	if la := len(res.Aliases); la != AddedServersCount {
		t.Fatalf("want aliases count = %d; got: %d", AddedServersCount, la)
	}

	// Check generated aliases which must be unique and not empty.
	aliasA := res.Aliases[idA]
	if aliasA == "" {
		t.Errorf("want aliasA != ``; got ``")
	}
	aliasB := res.Aliases[idB]
	if aliasB == "" {
		t.Errorf("want aliasB != ``; got ``")
	}
	if aliasA == aliasB {
		t.Errorf("want aliasA != aliasB; got %s == %s", aliasA, aliasB)
	}

	for i := 0; i < AddedServersCount; i++ {
		if err := builder.WaitForBuild(time.Second); err != nil {
			t.Fatalf("want err = nil; got %v", err)
		}
	}

	// Already added, update statuses; don't change aliases.
	res, err = g.Create(req)
	if err != nil {
		t.Fatalf("want err = nil; got %v", err)
	}

	if aliasA != res.Aliases[idA] {
		t.Errorf("want aliasA = %s; got %s", aliasA, res.Aliases[idA])
	}
	if aliasB != res.Aliases[idB] {
		t.Errorf("want aliasB = %s; got %s", aliasB, res.Aliases[idB])
	}

	// Machines were pinged and they clients were build.
	statuses := map[machine.ID]machine.Status{
		idA: machine.Status{State: machine.StateOffline},
		idB: machine.Status{State: machine.StateOnline},
	}
	if !reflect.DeepEqual(statuses, res.Statuses) {
		t.Fatalf("want statuses = %#v; got %#v", statuses, res.Statuses)
	}
}
Example #9
0
func TestID(t *testing.T) {
	var (
		idA = machine.ID("servA")
		idB = machine.ID("servB")

		ipA = machine.Addr{Network: "ip", Value: "52.24.123.32", UpdatedAt: time.Now()}
		ipB = machine.Addr{Network: "ip", Value: "10.0.1.16", UpdatedAt: time.Now()}
	)

	g, err := New(testOptions(testutil.NewBuilder(nil)))
	if err != nil {
		t.Fatalf("want err = nil; got %v", err)
	}
	defer g.Close()

	req := &CreateRequest{
		Addresses: map[machine.ID][]machine.Addr{
			idA: {ipA},
			idB: {ipB},
		},
	}

	res, err := g.Create(req)
	if err != nil {
		t.Fatalf("want err = nil; got %v", err)
	}

	tests := map[string]struct {
		Identifier string
		ID         machine.ID
		Found      bool
	}{
		"by ID": {
			Identifier: string(idA),
			ID:         idA,
			Found:      true,
		},
		"by alias": {
			Identifier: res.Aliases[idB],
			ID:         idB,
			Found:      true,
		},
		"by IP": {
			Identifier: ipA.Value,
			ID:         idA,
			Found:      true,
		},
		"not found alias": {
			Identifier: "unknown_alias",
			ID:         "",
			Found:      false,
		},
		"not found IP": {
			Identifier: "127.0.0.12",
			ID:         "",
			Found:      false,
		},
		"not found TCP": {
			Identifier: ipB.Value + ":8080",
			ID:         "",
			Found:      false,
		},
	}

	for name, test := range tests {
		test := test // capture range variable.
		t.Run(name, func(t *testing.T) {
			t.Parallel()
			req := &IDRequest{
				Identifier: test.Identifier,
			}

			res, err := g.ID(req)
			if (err == nil) != test.Found {
				t.Fatalf("want (err == nil) = %t; got err: %v", test.Found, err)
			}

			if !test.Found {
				return
			}

			if res.ID != test.ID {
				t.Fatalf("want ID = %v; got %v", test.ID, res.ID)
			}
		})
	}
}