lnd/kvdb/etcd/embed.go
Andras Banki-Horvath 6c2d8bb176
etcd: enable optional log file for embedded etcd log output
In this commit we add an extra config for enabling logging to an
external file when using embedded etcd. This can be useful when running
integration tests to see more details about etcd related issues.
2021-09-10 14:40:54 +02:00

116 lines
3 KiB
Go

// +build kvdb_etcd
package etcd
import (
"fmt"
"net"
"net/url"
"sync/atomic"
"time"
"go.etcd.io/etcd/server/v3/embed"
)
const (
// readyTimeout is the time until the embedded etcd instance should start.
readyTimeout = 10 * time.Second
// defaultEtcdPort is the start of the range for listening ports of
// embedded etcd servers. Ports are monotonically increasing starting
// from this number and are determined by the results of getFreePort().
defaultEtcdPort = 2379
// defaultNamespace is the namespace we'll use in our embedded etcd
// instance. Since it is only used for testing, we'll use the namespace
// name "test/" for this. Note that the namespace can be any string,
// the trailing / is not required.
defaultNamespace = "test/"
)
var (
// lastPort is the last port determined to be free for use by a new
// embedded etcd server. It should be used atomically.
lastPort uint32 = defaultEtcdPort
)
// getFreePort returns the first port that is available for listening by a new
// embedded etcd server. It panics if no port is found and the maximum available
// TCP port is reached.
func getFreePort() int {
port := atomic.AddUint32(&lastPort, 1)
for port < 65535 {
// If there are no errors while attempting to listen on this
// port, close the socket and return it as available.
addr := fmt.Sprintf("127.0.0.1:%d", port)
l, err := net.Listen("tcp4", addr)
if err == nil {
err := l.Close()
if err == nil {
return int(port)
}
}
port = atomic.AddUint32(&lastPort, 1)
}
// No ports available? Must be a mistake.
panic("no ports available for listening")
}
// NewEmbeddedEtcdInstance creates an embedded etcd instance for testing,
// listening on random open ports. Returns the backend config and a cleanup
// func that will stop the etcd instance.
func NewEmbeddedEtcdInstance(path string, clientPort, peerPort uint16,
logFile string) (*Config, func(), error) {
cfg := embed.NewConfig()
cfg.Dir = path
// To ensure that we can submit large transactions.
cfg.MaxTxnOps = 8192
cfg.MaxRequestBytes = 16384 * 1024
cfg.Logger = "zap"
if logFile != "" {
cfg.LogLevel = "info"
cfg.LogOutputs = []string{logFile}
} else {
cfg.LogLevel = "error"
}
// Listen on random free ports if no ports were specified.
if clientPort == 0 {
clientPort = uint16(getFreePort())
}
if peerPort == 0 {
peerPort = uint16(getFreePort())
}
clientURL := fmt.Sprintf("127.0.0.1:%d", clientPort)
peerURL := fmt.Sprintf("127.0.0.1:%d", peerPort)
cfg.LCUrls = []url.URL{{Host: clientURL}}
cfg.LPUrls = []url.URL{{Host: peerURL}}
etcd, err := embed.StartEtcd(cfg)
if err != nil {
return nil, nil, err
}
select {
case <-etcd.Server.ReadyNotify():
case <-time.After(readyTimeout):
etcd.Close()
return nil, nil,
fmt.Errorf("etcd failed to start after: %v", readyTimeout)
}
connConfig := &Config{
Host: "http://" + clientURL,
InsecureSkipVerify: true,
Namespace: defaultNamespace,
}
return connConfig, func() {
etcd.Close()
}, nil
}