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()) } }
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") } }
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)) } }
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) } }
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) } }
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()) } }
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) } }
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) } }
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) } }) } }