Skip to content

Commit fcc8b35

Browse files
committed
Add a way to create HostInfo objects for testing purposes
It is not possible to test implementations of public gocql interfaces such as HostFilter because it requires creating HostInfo objects (HostInfo has private fields). With this change, it is now possible to create HostInfo objects from system.local / system.peers rows using NewTestHostInfoFromRow(). Patch by João Reis; reviewed by James Hartig for CASSGO-71
1 parent f5fe483 commit fcc8b35

File tree

7 files changed

+100
-41
lines changed

7 files changed

+100
-41
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2020
Query.SetKeyspace(), Query.WithNowInSeconds(), Batch.SetKeyspace(), Batch.WithNowInSeconds() (CASSGO-1)
2121
- Externally-defined type registration (CASSGO-43)
2222
- Add Query and Batch to ObservedQuery and ObservedBatch (CASSGO-73)
23+
- Add way to create HostInfo objects for testing purposes (CASSGO-71)
2324

2425
### Changed
2526

@@ -47,6 +48,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
4748
- NativeType removed (CASSGO-43)
4849
- `New` and `NewWithError` removed and replaced with `Zero` (CASSGO-43)
4950
- Changes to Query and Batch to make them safely reusable (CASSGO-22)
51+
- HostInfo.SetHostID is no longer exported (CASSGO-71)
5052

5153
### Fixed
5254

conn.go

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1938,11 +1938,7 @@ func (c *Conn) awaitSchemaAgreement(ctx context.Context) (err error) {
19381938

19391939
for _, row := range rows {
19401940
var host *HostInfo
1941-
host, err = NewHostInfo(c.host.ConnectAddress(), c.session.cfg.Port)
1942-
if err != nil {
1943-
goto cont
1944-
}
1945-
host, err = c.session.hostInfoFromMap(row, host)
1941+
host, err = c.session.newHostInfoFromMap(c.host.ConnectAddress(), c.session.cfg.Port, row)
19461942
if err != nil {
19471943
goto cont
19481944
}

control.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ func hostInfo(addr string, defaultPort int) ([]*HostInfo, error) {
146146

147147
// Check if host is a literal IP address
148148
if ip := net.ParseIP(host); ip != nil {
149-
h, err := NewHostInfo(ip, port)
149+
h, err := NewHostInfoFromAddrPort(ip, port)
150150
if err != nil {
151151
return nil, err
152152
}
@@ -176,7 +176,7 @@ func hostInfo(addr string, defaultPort int) ([]*HostInfo, error) {
176176
}
177177

178178
for _, ip := range ips {
179-
h, err := NewHostInfo(ip, port)
179+
h, err := NewHostInfoFromAddrPort(ip, port)
180180
if err != nil {
181181
return nil, err
182182
}

host_source.go

Lines changed: 64 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,8 @@ func (c cassVersion) nodeUpDelay() time.Duration {
155155
return 10 * time.Second
156156
}
157157

158+
// HostInfo represents a server Host/Node. You can create a HostInfo object with either NewHostInfoFromAddrPort or
159+
// NewTestHostInfoFromRow.
158160
type HostInfo struct {
159161
// TODO(zariel): reduce locking maybe, not all values will change, but to ensure
160162
// that we are thread safe use a mutex to access all fields.
@@ -181,9 +183,12 @@ type HostInfo struct {
181183
tokens []string
182184
}
183185

184-
// NewHostInfo creates HostInfo with provided connectAddress and port.
186+
// NewHostInfoFromAddrPort creates HostInfo with provided connectAddress and port.
185187
// It returns an error if addr is invalid.
186-
func NewHostInfo(addr net.IP, port int) (*HostInfo, error) {
188+
//
189+
// If you're looking for a way to create a HostInfo object with more than just an address and port for
190+
// testing purposes then you can use NewTestHostInfoFromRow
191+
func NewHostInfoFromAddrPort(addr net.IP, port int) (*HostInfo, error) {
187192
if !validIpAddr(addr) {
188193
return nil, errors.New("invalid host address")
189194
}
@@ -251,7 +256,7 @@ func (h *HostInfo) nodeToNodeAddress() net.IP {
251256
return net.IPv4zero
252257
}
253258

254-
// Returns the address that should be used to connect to the host.
259+
// ConnectAddress Returns the address that should be used to connect to the host.
255260
// If you wish to override this, use an AddressTranslator
256261
func (h *HostInfo) ConnectAddress() net.IP {
257262
h.mu.RLock()
@@ -305,7 +310,7 @@ func (h *HostInfo) HostID() string {
305310
return h.hostId
306311
}
307312

308-
func (h *HostInfo) SetHostID(hostID string) {
313+
func (h *HostInfo) setHostID(hostID string) {
309314
h.mu.Lock()
310315
defer h.mu.Unlock()
311316
h.hostId = hostID
@@ -492,17 +497,42 @@ func checkSystemSchema(control *controlConn) (bool, error) {
492497
return true, nil
493498
}
494499

495-
func (s *Session) newHostInfoFromMap(addr net.IP, port int, row map[string]interface{}) (*HostInfo, error) {
496-
return s.hostInfoFromMap(row, &HostInfo{connectAddress: addr, port: port})
497-
}
498-
499500
// Given a map that represents a row from either system.local or system.peers
500501
// return as much information as we can in *HostInfo
501-
func (s *Session) hostInfoFromMap(row map[string]interface{}, host *HostInfo) (*HostInfo, error) {
502+
func (s *Session) newHostInfoFromMap(addr net.IP, port int, row map[string]interface{}) (*HostInfo, error) {
503+
return newHostInfoFromRow(s, addr, port, row)
504+
}
505+
506+
// NewTestHostInfoFromRow creates a new HostInfo object from a system.peers or system.local row. The port
507+
// defaults to 9042.
508+
//
509+
// You can create a HostInfo object for testing purposes using this function:
510+
//
511+
// Example usage:
512+
//
513+
// row := map[string]interface{}{
514+
// "broadcast_address": net.ParseIP("10.0.0.1"),
515+
// "listen_address": net.ParseIP("10.0.0.1"),
516+
// "rpc_address": net.ParseIP("10.0.0.1"),
517+
// "peer": net.ParseIP("10.0.0.1"), // system.peers only
518+
// "data_center": "dc1",
519+
// "rack": "rack1",
520+
// "host_id": MustRandomUUID(), // can also use ParseUUID("550e8400-e29b-41d4-a716-446655440000")
521+
// "release_version": "4.0.0",
522+
// "native_port": 9042,
523+
// }
524+
// host, err := NewTestHostInfoFromRow(row)
525+
func NewTestHostInfoFromRow(row map[string]interface{}) (*HostInfo, error) {
526+
return newHostInfoFromRow(nil, nil, 9042, row)
527+
}
528+
529+
func newHostInfoFromRow(s *Session, defaultAddr net.IP, defaultPort int, row map[string]interface{}) (*HostInfo, error) {
502530
const assertErrorMsg = "Assertion failed for %s, type was %T"
503531
var ok bool
504532

505-
// Default to our connected port if the cluster doesn't have port information
533+
host := &HostInfo{connectAddress: defaultAddr, port: defaultPort}
534+
535+
// Process all fields from the row
506536
for key, value := range row {
507537
switch key {
508538
case "data_center":
@@ -606,18 +636,34 @@ func (s *Session) hostInfoFromMap(row map[string]interface{}, host *HostInfo) (*
606636
}
607637
host.schemaVersion = schemaVersion.String()
608638
}
609-
// TODO(thrawn01): Add 'port'? once CASSANDRA-7544 is complete
610-
// Not sure what the port field will be called until the JIRA issue is complete
611639
}
612640

613-
ip, port := s.cfg.translateAddressPort(host.ConnectAddress(), host.port)
614-
if !validIpAddr(ip) {
615-
return nil, fmt.Errorf("invalid host address (before translation: %v:%v, after translation: %v:%v)", host.ConnectAddress(), host.port, ip.String(), port)
641+
// Determine the connect address from available addresses
642+
if validIpAddr(host.rpcAddress) {
643+
host.connectAddress = host.rpcAddress
644+
} else if validIpAddr(host.preferredIP) {
645+
host.connectAddress = host.preferredIP
646+
} else if validIpAddr(host.broadcastAddress) {
647+
host.connectAddress = host.broadcastAddress
648+
} else if validIpAddr(host.peer) {
649+
host.connectAddress = host.peer
616650
}
617-
host.connectAddress = ip
618-
host.port = port
619651

620-
return host, nil
652+
if s != nil && s.cfg.AddressTranslator != nil {
653+
ip, port := s.cfg.translateAddressPort(host.ConnectAddress(), host.port)
654+
if !validIpAddr(ip) {
655+
return nil, fmt.Errorf("invalid host address (before translation: %v:%v, after translation: %v:%v)", host.ConnectAddress(), host.port, ip.String(), port)
656+
}
657+
host.connectAddress = ip
658+
host.port = port
659+
}
660+
661+
if validIpAddr(host.connectAddress) {
662+
host.hostname = host.connectAddress.String()
663+
return host, nil
664+
} else {
665+
return nil, errors.New("invalid host address")
666+
}
621667
}
622668

623669
func (s *Session) hostInfoFromIter(iter *Iter, connectAddress net.IP, defaultPort int) (*HostInfo, error) {

hostpool/hostpool_test.go

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,21 +17,33 @@ func TestHostPolicy_HostPool(t *testing.T) {
1717
policy := HostPoolHostPolicy(hostpool.New(nil))
1818

1919
//hosts := []*gocql.HostInfo{
20-
// {hostId: "0", connectAddress: net.IPv4(10, 0, 0, 0)},
21-
// {hostId: "1", connectAddress: net.IPv4(10, 0, 0, 1)},
20+
// {hostId: "f1935733-af5f-4995-bd1e-94a7a3e67bfd", connectAddress: net.ParseIP("10.0.0.0")},
21+
// {hostId: "93ca4489-b322-4fda-b5a5-12d4436271df", connectAddress: net.ParseIP("10.0.0.1")},
2222
//}
23+
firstHostId, err1 := gocql.ParseUUID("f1935733-af5f-4995-bd1e-94a7a3e67bfd")
24+
secondHostId, err2 := gocql.ParseUUID("93ca4489-b322-4fda-b5a5-12d4436271df")
2325

24-
firstHost, err := gocql.NewHostInfo(net.IPv4(10, 0, 0, 0), 9042)
26+
if err1 != nil || err2 != nil {
27+
t.Fatal(err1, err2)
28+
}
29+
30+
firstHost, err := gocql.NewTestHostInfoFromRow(
31+
map[string]interface{}{
32+
"peer": net.ParseIP("10.0.0.0"),
33+
"native_port": 9042,
34+
"host_id": firstHostId})
2535
if err != nil {
2636
t.Errorf("Error creating first host: %v", err)
2737
}
28-
firstHost.SetHostID("0")
2938

30-
secHost, err := gocql.NewHostInfo(net.IPv4(10, 0, 0, 1), 9042)
39+
secHost, err := gocql.NewTestHostInfoFromRow(
40+
map[string]interface{}{
41+
"peer": net.ParseIP("10.0.0.1"),
42+
"native_port": 9042,
43+
"host_id": secondHostId})
3144
if err != nil {
3245
t.Errorf("Error creating second host: %v", err)
3346
}
34-
secHost.SetHostID("1")
3547
hosts := []*gocql.HostInfo{firstHost, secHost}
3648
// Using set host to control the ordering of the hosts as calling "AddHost" iterates the map
3749
// which will result in an unpredictable ordering
@@ -41,26 +53,26 @@ func TestHostPolicy_HostPool(t *testing.T) {
4153
// interleaved iteration should always increment the host
4254
iter := policy.Pick(nil)
4355
actualA := iter()
44-
if actualA.Info().HostID() != "0" {
45-
t.Errorf("Expected hosts[0] but was hosts[%s]", actualA.Info().HostID())
56+
if actualA.Info().HostID() != firstHostId.String() {
57+
t.Errorf("Expected first host id but was %s", actualA.Info().HostID())
4658
}
4759
actualA.Mark(nil)
4860

4961
actualB := iter()
50-
if actualB.Info().HostID() != "1" {
51-
t.Errorf("Expected hosts[1] but was hosts[%s]", actualB.Info().HostID())
62+
if actualB.Info().HostID() != secondHostId.String() {
63+
t.Errorf("Expected second host id but was %s", actualB.Info().HostID())
5264
}
5365
actualB.Mark(fmt.Errorf("error"))
5466

5567
actualC := iter()
56-
if actualC.Info().HostID() != "0" {
57-
t.Errorf("Expected hosts[0] but was hosts[%s]", actualC.Info().HostID())
68+
if actualC.Info().HostID() != firstHostId.String() {
69+
t.Errorf("Expected first host id but was %s", actualC.Info().HostID())
5870
}
5971
actualC.Mark(nil)
6072

6173
actualD := iter()
62-
if actualD.Info().HostID() != "0" {
63-
t.Errorf("Expected hosts[0] but was hosts[%s]", actualD.Info().HostID())
74+
if actualD.Info().HostID() != firstHostId.String() {
75+
t.Errorf("Expected first host id but was %s", actualD.Info().HostID())
6476
}
6577
actualD.Mark(nil)
6678
}

session.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,7 @@ func (s *Session) init() error {
264264
// by internal logic.
265265
// Associate random UUIDs here with all hosts missing this information.
266266
if len(host.HostID()) == 0 {
267-
host.SetHostID(MustRandomUUID().String())
267+
host.setHostID(MustRandomUUID().String())
268268
}
269269
}
270270

@@ -2134,7 +2134,7 @@ type ObservedQuery struct {
21342134
// Rows is not used in batch queries and remains at the default value
21352135
Rows int
21362136

2137-
// Host is the informations about the host that performed the query
2137+
// Host is the information about the host that performed the query
21382138
Host *HostInfo
21392139

21402140
// The metrics per this host

snappy/compressor_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
//go:build all || unit
2+
// +build all unit
3+
14
/*
25
* Licensed to the Apache Software Foundation (ASF) under one
36
* or more contributor license agreements. See the NOTICE file

0 commit comments

Comments
 (0)