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 TestAliasesMachineID(t *testing.T) { m := map[machine.ID]string{ "ID_1": "blue_banana", "ID_2": "red_orange1", "ID_3": "white_mango", "ID_4": "black_tomato2", "ID_5": "silver_kiwi", } as := aliases.New() for id, alias := range m { if err := as.Add(id, alias); err != nil { t.Fatalf("want err = nil; got %v", err) } } tests := map[string]struct { Alias string Expected machine.ID NotFound bool }{ "full alias": { Alias: "white_mango", Expected: machine.ID("ID_3"), NotFound: false, }, "machine id": { Alias: "ID_5", Expected: machine.ID("ID_5"), NotFound: false, }, "unknown": { Alias: "green_kiwi", Expected: machine.ID(""), NotFound: true, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { id, err := as.MachineID(test.Alias) if (err == machine.ErrMachineNotFound) != test.NotFound { t.Fatalf("want err machine not found = %t; got %v", test.NotFound, err) } if id != test.Expected { t.Fatalf("want id = %v; got %v", test.Expected, id) } }) } }
// MachineID checks if there is a machine ID that is binded to provided alias. // If yes, the machine ID is returned. machine.ErrMachineNotFound is returned // if there is no machine ID with provided alias. // // TODO: Add support for shortcuts like rb1 == red_banana1 etc. func (a *Aliases) MachineID(alias string) (machine.ID, error) { a.mu.RLock() defer a.mu.RUnlock() for id, a := range a.m { // provided alias can be the machine ID itself. if machine.ID(alias) == id || alias == a { return id, nil } } return "", machine.ErrMachineNotFound }
func TestAddressesMachineIDDuplicated(t *testing.T) { makeAddr := func(updatedAt time.Time) machine.Addr { return machine.Addr{ Network: "ip", Value: "52.254.159.36", UpdatedAt: updatedAt, } } machines := []struct { ID machine.ID Addr machine.Addr }{ { ID: "ID_1", Addr: makeAddr(time.Date(2010, time.May, 1, 0, 0, 0, 0, time.UTC)), }, { ID: "ID_2", Addr: makeAddr(time.Date(2012, time.May, 1, 0, 0, 0, 0, time.UTC)), }, { ID: "ID_3", Addr: makeAddr(time.Date(2011, time.May, 1, 0, 0, 0, 0, time.UTC)), }, } addrs := addresses.New() machineIDs := make(map[machine.ID]struct{}) for i, machine := range machines { machineIDs[machine.ID] = struct{}{} if err := addrs.Add(machine.ID, machine.Addr); err != nil { t.Fatalf("want err = nil; got %v (i:%v)", err, i) } } if len(machineIDs) != len(addrs.Registered()) { t.Fatal("want %d machines; got %d", len(machineIDs), len(addrs.Registered())) } id, err := addrs.MachineID(makeAddr(time.Time{})) if err != nil { t.Errorf("want err = nil; got %v", err) } if wantID := machine.ID("ID_2"); wantID != id { t.Errorf("want machine ID = %v; got %v", wantID, id) } }
func TestAliasesCreate(t *testing.T) { const wantAliases = 500 m := make(map[string]struct{}) as := aliases.New() for i := 0; i < wantAliases; i++ { alias, err := as.Create(machine.ID(i)) if err != nil { t.Fatalf("want err = nil; got %v", err) } m[alias] = struct{}{} // Second creation for the same ID should be no-op. alias, err = as.Create(machine.ID(i)) if err != nil { t.Fatalf("want err = nil; got %v", err) } m[alias] = struct{}{} } if len(m) != wantAliases { t.Fatalf("want %d aliases; got %d", wantAliases, len(m)) } }
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) } }
// List retrieves user's machines from kloud. func List(options *ListOptions) ([]*Info, error) { var ( listReq = stack.MachineListRequest{} listRes = stack.MachineListResponse{} ) // Get info from kloud. if err := kloud.Call("machine.list", &listReq, &listRes); err != nil { return nil, err } // Register machines to klient and get aliases. // // TODO(ppknap): this is copied from klientctl old list and will be reworked. k, err := klient.CreateKlientWithDefaultOpts() if err != nil { fmt.Fprintln(os.Stderr, "Error creating klient:", err) return nil, err } if err := k.Dial(); err != nil { fmt.Fprintln(os.Stderr, "Error dialing klient:", err) return nil, err } createReq := machinegroup.CreateRequest{ Addresses: make(map[kmachine.ID][]kmachine.Addr), } for _, m := range listRes.Machines { createReq.Addresses[kmachine.ID(m.ID)] = []kmachine.Addr{ { Network: "ip", Value: m.IP, UpdatedAt: time.Now(), }, { Network: "kite", Value: m.QueryString, UpdatedAt: time.Now(), }, { Network: "http", Value: m.RegisterURL, UpdatedAt: time.Now(), }, } } createRaw, err := k.Tell("machine.create", createReq) if err != nil { return nil, err } createRes := machinegroup.CreateResponse{} if err := createRaw.Unmarshal(&createRes); err != nil { return nil, err } infos := make([]*Info, len(listRes.Machines)) for i, m := range listRes.Machines { infos[i] = &Info{ ID: m.ID, Alias: createRes.Aliases[kmachine.ID(m.ID)], Team: m.Team, Stack: m.Stack, Provider: m.Provider, Label: m.Label, IP: m.IP, QueryString: m.QueryString, RegisterURL: m.RegisterURL, CreatedAt: m.CreatedAt, Status: kmachine.MergeStatus(kmachine.Status{ State: fromMachineStateString(m.Status.State), Reason: m.Status.Reason, Since: m.Status.ModifiedAt, }, createRes.Statuses[kmachine.ID(m.ID)]), Username: machineUserFromUsers(m.Users), Owner: ownerFromUsers(m.Users), } } // Sort items before we return. sort.Sort(InfoSlice(infos)) return infos, nil }
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) } }) } }