Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion network/wsNetwork.go
Original file line number Diff line number Diff line change
Expand Up @@ -1406,7 +1406,7 @@ func (wn *WebsocketNetwork) peersToPing() []*wsPeer {
}

func (wn *WebsocketNetwork) getDNSAddrs(dnsBootstrap string) []string {
srvPhonebook, err := tools_network.ReadFromSRV("algobootstrap", dnsBootstrap, wn.config.FallbackDNSResolverAddress)
srvPhonebook, err := tools_network.ReadFromSRV("algobootstrap", "tcp", dnsBootstrap, wn.config.FallbackDNSResolverAddress)
if err != nil {
// only log this warning on testnet or devnet
if wn.NetworkID == config.Devnet || wn.NetworkID == config.Testnet {
Expand Down
6 changes: 5 additions & 1 deletion tools/network/bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,16 @@ import (
)

// ReadFromSRV is a helper to collect SRV addresses for a given name.
func ReadFromSRV(service string, name string, fallbackDNSResolverAddress string) (addrs []string, err error) {
func ReadFromSRV(service string, protocol string, name string, fallbackDNSResolverAddress string) (addrs []string, err error) {
log := logging.Base()
if name == "" {
log.Debug("no dns lookup due to empty name")
return
}
if protocol != "tcp" && protocol != "udp" && protocol != "tls" {
err = fmt.Errorf("unsupported protocol '%s' specified", protocol)
return
}

_, records, sysLookupErr := net.LookupSRV(service, "tcp", name)
if sysLookupErr != nil {
Expand Down
88 changes: 75 additions & 13 deletions tools/network/telemetryURIUpdateService.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,51 @@
package network

import (
"net/url"
"strings"
"time"

"github.com/algorand/go-algorand/config"
"github.com/algorand/go-algorand/logging"
"github.com/algorand/go-algorand/protocol"
)

type telemertySrvReader interface {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/telemerty/telemetry

readFromSRV(protocol string, bootstrapID string) (addrs []string, err error)
}

type telemetryURIUpdater struct {
interval time.Duration
cfg config.Local
genesisNetwork protocol.NetworkID
log logging.Logger
abort chan struct{}
srvReader telemertySrvReader
}

// StartTelemetryURIUpdateService starts a go routine which queries SRV records for a telemetry URI every <interval>
func StartTelemetryURIUpdateService(interval time.Duration, cfg config.Local, genesisNetwork protocol.NetworkID, log logging.Logger, abort chan struct{}) {
updater := &telemetryURIUpdater{
interval: interval,
cfg: cfg,
genesisNetwork: genesisNetwork,
log: log,
abort: abort,
}
updater.srvReader = updater
updater.Start()

}
func (t *telemetryURIUpdater) Start() {
go func() {
ticker := time.NewTicker(interval)
ticker := time.NewTicker(t.interval)
defer ticker.Stop()

updateTelemetryURI := func() {
endpoint := lookupTelemetryEndpoint(cfg, genesisNetwork, log)
endpointURL := t.lookupTelemetryURL()

if endpoint != "" && endpoint != log.GetTelemetryURI() {
log.UpdateTelemetryURI(endpoint)
if endpointURL != nil && endpointURL.String() != t.log.GetTelemetryURI() {
t.log.UpdateTelemetryURI(endpointURL.String())
}
}

Expand All @@ -44,27 +71,62 @@ func StartTelemetryURIUpdateService(interval time.Duration, cfg config.Local, ge
select {
case <-ticker.C:
updateTelemetryURI()
case <-abort:
case <-t.abort:
return
}
}
}()
}

func lookupTelemetryEndpoint(cfg config.Local, genesisNetwork protocol.NetworkID, log logging.Logger) string {
bootstrapArray := cfg.DNSBootstrapArray(genesisNetwork)
func (t *telemetryURIUpdater) lookupTelemetryURL() (url *url.URL) {
bootstrapArray := t.cfg.DNSBootstrapArray(t.genesisNetwork)
bootstrapArray = append(bootstrapArray, "default.algodev.network")
for _, bootstrapID := range bootstrapArray {
addrs, err := ReadFromSRV("telemetry", bootstrapID, cfg.FallbackDNSResolverAddress)
addrs, err := t.srvReader.readFromSRV("tls", bootstrapID)
if err != nil {
log.Infof("An issue occurred reading telemetry entry for '%s': %v", bootstrapID, err)
t.log.Infof("An issue occurred reading telemetry entry for '_telemetry._tls.%s': %v", bootstrapID, err)
} else if len(addrs) == 0 {
log.Infof("No telemetry entry for: '%s'", bootstrapID)
t.log.Infof("No telemetry entry for: '_telemetry._tls.%s'", bootstrapID)
} else {
return addrs[0]
for _, addr := range addrs {
// the add that we received from ReadFromSRV contains host:port, we need to prefix that with the schema. since it's the tls, we want to use https.
url, err = url.Parse("https://" + addr)
if err != nil {
t.log.Infof("a telemetry endpoint '%s' was retrieved for '_telemerty._tls.%s'. This does not seems to be a valid endpoint and will be ignored(%v).", addr, bootstrapID, err)
continue
}
return url
}
}

addrs, err = t.srvReader.readFromSRV("tcp", bootstrapID)
if err != nil {
t.log.Infof("An issue occurred reading telemetry entry for '_telemetry._tcp.%s': %v", bootstrapID, err)
} else if len(addrs) == 0 {
t.log.Infof("No telemetry entry for: '_telemetry._tcp.%s'", bootstrapID)
} else {
for _, addr := range addrs {
if strings.HasPrefix(addr, "https://") {
// the add that we received from ReadFromSRV should contains host:port. however, in some cases, it migth contains a https prefix, where we want to take it as is.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some typos in comments. add -> url in most of them, in this comment I think you mean "should contains" -> "should not contain"

url, err = url.Parse(addr)
} else {
// the add that we received from ReadFromSRV contains host:port, we need to prefix that with the schema. since it's the tcp, we want to use http.
url, err = url.Parse("http://" + addr)
}

if err != nil {
t.log.Infof("a telemetry endpoint '%s' was retrieved for '_telemerty._tcp.%s'. This does not seems to be a valid endpoint and will be ignored(%v).", addr, bootstrapID, err)
continue
}
return url
}
}
}

log.Warn("No telemetry endpoint was found.")
return ""
t.log.Warn("No telemetry endpoint was found.")
return nil
}

func (t *telemetryURIUpdater) readFromSRV(protocol string, bootstrapID string) (addrs []string, err error) {
return ReadFromSRV("telemetry", protocol, bootstrapID, t.cfg.FallbackDNSResolverAddress)
}
90 changes: 90 additions & 0 deletions tools/network/telemetryURIUpdateService_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// Copyright (C) 2019 Algorand, Inc.
// This file is part of go-algorand
//
// go-algorand is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// go-algorand is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with go-algorand. If not, see <https://www.gnu.org/licenses/>.

package network

import (
"fmt"
"testing"

"github.com/stretchr/testify/require"

"github.com/algorand/go-algorand/config"
"github.com/algorand/go-algorand/logging"
"github.com/algorand/go-algorand/protocol"
)

type telemetryURIUpdaterTest struct {
telemetryURIUpdater
readFromSRVResults map[string][]string
}

func (t *telemetryURIUpdaterTest) readFromSRV(protocol string, bootstrapID string) (addrs []string, err error) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this test strategy, great approach 👍

if addr, ok := t.readFromSRVResults[protocol+bootstrapID]; ok {
return addr, nil
}
fmt.Printf("no result for %s %s\n", protocol, bootstrapID)
return nil, fmt.Errorf("no cached results")
}

func makeTelemetryURIUpdaterTest(genesisNetwork protocol.NetworkID) *telemetryURIUpdaterTest {
t := &telemetryURIUpdaterTest{
telemetryURIUpdater: telemetryURIUpdater{
cfg: config.GetDefaultLocal(),
log: logging.Base(),
genesisNetwork: genesisNetwork,
},
readFromSRVResults: make(map[string][]string),
}
t.srvReader = t
return t
}

func (t *telemetryURIUpdaterTest) add(protocol, bootstrap string, addrs []string) {
t.readFromSRVResults[protocol+bootstrap] = addrs
}

func TestTelemetryURILookup(t *testing.T) {

// trivial success case.
uriUpdater := makeTelemetryURIUpdaterTest(config.Devnet)
uriUpdater.add("tcp", "devnet.algodev.network", []string{"myhost:4160"})
uri := uriUpdater.lookupTelemetryURL()
require.NotNil(t, uri)
require.Equal(t, "http://myhost:4160", uri.String())

// check https prefixing
uriUpdater = makeTelemetryURIUpdaterTest(config.Devnet)
uriUpdater.add("tcp", "devnet.algodev.network", []string{"https://myhost:4160"})
uri = uriUpdater.lookupTelemetryURL()
require.NotNil(t, uri)
require.Equal(t, "https://myhost:4160", uri.String())

// check https priority
uriUpdater = makeTelemetryURIUpdaterTest(config.Devnet)
uriUpdater.add("tcp", "devnet.algodev.network", []string{"myhost2:4160"})
uriUpdater.add("tls", "devnet.algodev.network", []string{"myhost1:4160"})
uri = uriUpdater.lookupTelemetryURL()
require.NotNil(t, uri)
require.Equal(t, "https://myhost1:4160", uri.String())

// check fallback
uriUpdater = makeTelemetryURIUpdaterTest(config.Devnet)
uriUpdater.add("tcp", "default.algodev.network", []string{"fallbackhost:8123"})
uri = uriUpdater.lookupTelemetryURL()
require.NotNil(t, uri)
require.Equal(t, "http://fallbackhost:8123", uri.String())
}