dnsseed: implement better config options (#1)
This commit is contained in:
parent
f6c1b71dd0
commit
4313ef4778
110
dnsseed/setup.go
110
dnsseed/setup.go
|
@ -1,8 +1,8 @@
|
||||||
package dnsseed
|
package dnsseed
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"net"
|
"net"
|
||||||
|
"net/url"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/caddyserver/caddy"
|
"github.com/caddyserver/caddy"
|
||||||
|
@ -14,62 +14,59 @@ import (
|
||||||
"github.com/zcashfoundation/dnsseeder/zcash/network"
|
"github.com/zcashfoundation/dnsseeder/zcash/network"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const pluginName = "dnsseed"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
log = clog.NewWithPlugin("dnsseed")
|
log = clog.NewWithPlugin(pluginName)
|
||||||
updateInterval = 15 * time.Minute
|
updateInterval = 15 * time.Minute
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() { plugin.Register("dnsseed", setup) }
|
func init() { plugin.Register(pluginName, setup) }
|
||||||
|
|
||||||
// setup is the function that gets called when the config parser see the token "dnsseed". Setup is responsible
|
// setup is the function that gets called when the config parser see the token(pluginName. Setup is responsible
|
||||||
// for parsing any extra options the example plugin may have. The first token this function sees is "dnsseed".
|
// for parsing any extra options the example plugin may have. The first token this function sees is(pluginName.
|
||||||
func setup(c *caddy.Controller) error {
|
func setup(c *caddy.Controller) error {
|
||||||
var rootArg, networkArg, hostArg string
|
// Automatically configure responsive zone
|
||||||
|
zone, err := url.Parse(c.Key)
|
||||||
c.Next() // Ignore "dnsseed" and give us the next token.
|
|
||||||
|
|
||||||
if !c.Args(&rootArg, &networkArg, &hostArg) {
|
|
||||||
return plugin.Error("dnsseed", c.ArgErr())
|
|
||||||
}
|
|
||||||
|
|
||||||
var magic network.Network
|
|
||||||
switch networkArg {
|
|
||||||
case "mainnet":
|
|
||||||
magic = network.Mainnet
|
|
||||||
case "testnet":
|
|
||||||
magic = network.Testnet
|
|
||||||
default:
|
|
||||||
return plugin.Error("dnsseed", c.Errf("Config error: expected {mainnet, testnet}, got %s", networkArg))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Automatically configure the responsive zone by network
|
|
||||||
zone := fmt.Sprintf("seeder.%s.%s.", networkArg, rootArg)
|
|
||||||
|
|
||||||
address, port, err := net.SplitHostPort(hostArg)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return plugin.Error("dnsseed", c.Errf("Config error: expected 'host:port', got %s", hostArg))
|
return c.Errf("couldn't parse zone from block identifer: %s", c.Key)
|
||||||
}
|
}
|
||||||
|
|
||||||
// XXX: If we wanted to register Prometheus metrics, this would be the place.
|
magic, interval, bootstrap, err := parseConfig(c)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if interval != 0 {
|
||||||
|
updateInterval = interval
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO If we wanted to register Prometheus metrics, this would be the place.
|
||||||
|
|
||||||
seeder, err := zcash.NewSeeder(magic)
|
seeder, err := zcash.NewSeeder(magic)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return plugin.Error("dnsseed", err)
|
return plugin.Error(pluginName, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO load from storage if we already know some peers
|
// TODO load from storage if we already know some peers
|
||||||
|
|
||||||
// Send the initial request for more addresses; spawns goroutines to process the responses.
|
log.Infof("Getting addresses from bootstrap peers %v", bootstrap)
|
||||||
// Ready() will flip to true once we've received and confirmed at least 10 peers.
|
|
||||||
|
|
||||||
log.Infof("Getting addresses from bootstrap peer %s:%s", address, port)
|
for _, s := range bootstrap {
|
||||||
|
address, port, err := net.SplitHostPort(s)
|
||||||
|
if err != nil {
|
||||||
|
return plugin.Error(pluginName, c.Errf("config error: expected 'host:port', got %s", s))
|
||||||
|
}
|
||||||
|
|
||||||
// Connect to the bootstrap peer
|
// Connect to the bootstrap peer
|
||||||
err = seeder.Connect(address, port)
|
err = seeder.Connect(address, port)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return plugin.Error("dnsseed", err)
|
return plugin.Error(pluginName, c.Errf("error connecting to %s:%s: %v", address, port, err))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Send the initial request for more addresses; spawns goroutines to process the responses.
|
||||||
|
// Ready() will flip to true once we've received and confirmed at least 10 peers.
|
||||||
seeder.RequestAddresses()
|
seeder.RequestAddresses()
|
||||||
seeder.DisconnectAllPeers()
|
seeder.DisconnectAllPeers()
|
||||||
|
|
||||||
|
@ -88,7 +85,7 @@ func setup(c *caddy.Controller) error {
|
||||||
dnsserver.GetConfig(c).AddPlugin(func(next plugin.Handler) plugin.Handler {
|
dnsserver.GetConfig(c).AddPlugin(func(next plugin.Handler) plugin.Handler {
|
||||||
return ZcashSeeder{
|
return ZcashSeeder{
|
||||||
Next: next,
|
Next: next,
|
||||||
Zones: []string{zone},
|
Zones: []string{zone.Hostname()},
|
||||||
seeder: seeder,
|
seeder: seeder,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -97,6 +94,43 @@ func setup(c *caddy.Controller) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseConfig(c *caddy.Controller) (magic network.Network, interval time.Duration, bootstrap []string, err error) {
|
||||||
|
c.Next() // skip the string "dnsseed"
|
||||||
|
|
||||||
|
for c.NextBlock() {
|
||||||
|
switch c.Val() {
|
||||||
|
case "network":
|
||||||
|
if !c.NextArg() {
|
||||||
|
return 0, 0, nil, plugin.Error(pluginName, c.SyntaxErr("no network specified"))
|
||||||
|
}
|
||||||
|
switch c.Val() {
|
||||||
|
case "mainnet":
|
||||||
|
magic = network.Mainnet
|
||||||
|
case "testnet":
|
||||||
|
magic = network.Testnet
|
||||||
|
default:
|
||||||
|
return 0, 0, nil, plugin.Error(pluginName, c.SyntaxErr("networks are {mainnet, testnet}"))
|
||||||
|
}
|
||||||
|
case "crawl_interval":
|
||||||
|
if !c.NextArg() {
|
||||||
|
return 0, 0, nil, plugin.Error(pluginName, c.SyntaxErr("no crawl interval specified"))
|
||||||
|
}
|
||||||
|
interval, err = time.ParseDuration(c.Val())
|
||||||
|
if err != nil || interval == 0 {
|
||||||
|
return 0, 0, nil, plugin.Error(pluginName, c.SyntaxErr("bad crawl_interval duration"))
|
||||||
|
}
|
||||||
|
case "bootstrap_peers":
|
||||||
|
bootstrap = c.RemainingArgs()
|
||||||
|
if len(bootstrap) == 0 {
|
||||||
|
plugin.Error(pluginName, c.SyntaxErr("no bootstrap peers specified"))
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return 0, 0, nil, plugin.Error(pluginName, c.SyntaxErr("unsupported option"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func runCrawl(seeder *zcash.Seeder) {
|
func runCrawl(seeder *zcash.Seeder) {
|
||||||
log.Infof("[%s] Beginning crawl", time.Now().Format("2006/01/02 15:04:05"))
|
log.Infof("[%s] Beginning crawl", time.Now().Format("2006/01/02 15:04:05"))
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
|
@ -1,26 +1,55 @@
|
||||||
package dnsseed
|
package dnsseed
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/caddyserver/caddy"
|
"github.com/caddyserver/caddy"
|
||||||
|
"github.com/zcashfoundation/dnsseeder/zcash/network"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestSetup tests the various things that should be parsed by setup.
|
// TestSetup tests the various things that should be parsed by setup.
|
||||||
func TestSetup(t *testing.T) {
|
func TestSetup(t *testing.T) {
|
||||||
c := caddy.NewTestController("dns", "dnsseed yolo.money mainnet 127.0.0.1:8233")
|
tt := []struct {
|
||||||
if err := setup(c); err != nil {
|
config string
|
||||||
if strings.Contains(err.Error(), "connection refused") {
|
isCorrect bool
|
||||||
// No local peer running
|
magic network.Network
|
||||||
// TODO: mock a local peer, which will be easier with an API rework in the zcash package
|
interval string
|
||||||
t.SkipNow()
|
bootstrap []string
|
||||||
}
|
}{
|
||||||
t.Fatalf("Expected no errors, but got: %v", err)
|
{`dnsseed`, false, 0, "0s", []string{}},
|
||||||
|
{`dnsseed mainnet`, false, 0, "0s", []string{}},
|
||||||
|
{`dnsseed { }`, false, 0, "0s", []string{}},
|
||||||
|
{`dnsseed { network }`, false, 0, "0s", []string{}},
|
||||||
|
{`dnsseed { network mainnet }`, false, network.Mainnet, "0s", []string{}},
|
||||||
|
{`dnsseed {
|
||||||
|
network testnet
|
||||||
|
crawl_interval 15s
|
||||||
|
bootstrap_peers
|
||||||
|
}`, true, network.Testnet, (time.Duration(15) * time.Second).String(), []string{},
|
||||||
|
},
|
||||||
|
{`dnsseed {
|
||||||
|
network testnet
|
||||||
|
crawl_interval 15s
|
||||||
|
bootstrap_peers 127.0.0.1:8233
|
||||||
|
}`, true, network.Testnet, (time.Duration(15) * time.Second).String(), []string{"127.0.0.1:8233"},
|
||||||
|
},
|
||||||
|
{`dnsseed {
|
||||||
|
network mainnet
|
||||||
|
crawl_interval 30m
|
||||||
|
bootstrap_peers 127.0.0.1:8233 127.0.0.2:8233
|
||||||
|
}`, true, network.Mainnet, (time.Duration(30) * time.Minute).String(), []string{"127.0.0.1:8233", "127.0.0.2:8233"},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
c = caddy.NewTestController("dns", "dnsseed boop snoot")
|
for _, test := range tt {
|
||||||
if err := setup(c); err == nil {
|
c := caddy.NewTestController("dns", test.config)
|
||||||
t.Fatalf("Expected errors, but got: %v", err)
|
magic, interval, bootstrap, err := parseConfig(c)
|
||||||
|
if err != nil && test.isCorrect {
|
||||||
|
t.Errorf("Unexpected error in test case `%s`: %v", test.config, err)
|
||||||
|
}
|
||||||
|
if magic != test.magic || interval.String() != test.interval || len(test.bootstrap) != len(bootstrap) {
|
||||||
|
t.Errorf("Input: %s Results: %v, %s, %s, %v", test.config, magic, interval, bootstrap, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue