Feature: Support multiple listeners in configuration
* add ipv6 listening addresses to the default config * ipfsproxy: support multiple listeners. Add default ipv6. * mm * restapi: support multiple listen addresses. enable ipv6 * cluster_config: format default listen addresses * commands: update for multiple listeners. Fix randomports for udp and ipv6. * ipfs-cluster-service: fix randomports test * multiple listeners: fix remaining tests * golint * Disable ipv6 in defaults It is not supported by docker by default. It is not supported in travis-CI build environments. User can enable it now manually. * proxy: disable ipv6 in test * ipfshttp: fix test Co-authored-by: @RubenKelevra <cyrond@gmail.com>
This commit is contained in:
parent
7986d94242
commit
531379b1d9
|
@ -7,6 +7,7 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
ipfsconfig "github.com/ipfs/go-ipfs-config"
|
||||||
"github.com/kelseyhightower/envconfig"
|
"github.com/kelseyhightower/envconfig"
|
||||||
ma "github.com/multiformats/go-multiaddr"
|
ma "github.com/multiformats/go-multiaddr"
|
||||||
|
|
||||||
|
@ -19,9 +20,13 @@ const (
|
||||||
minMaxHeaderBytes = 4096
|
minMaxHeaderBytes = 4096
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// DefaultListenAddrs contains the default listeners for the proxy.
|
||||||
|
var DefaultListenAddrs = []string{
|
||||||
|
"/ip4/127.0.0.1/tcp/9095",
|
||||||
|
}
|
||||||
|
|
||||||
// Default values for Config.
|
// Default values for Config.
|
||||||
const (
|
const (
|
||||||
DefaultListenAddr = "/ip4/127.0.0.1/tcp/9095"
|
|
||||||
DefaultNodeAddr = "/ip4/127.0.0.1/tcp/5001"
|
DefaultNodeAddr = "/ip4/127.0.0.1/tcp/5001"
|
||||||
DefaultNodeHTTPS = false
|
DefaultNodeHTTPS = false
|
||||||
DefaultReadTimeout = 0
|
DefaultReadTimeout = 0
|
||||||
|
@ -39,7 +44,7 @@ type Config struct {
|
||||||
config.Saver
|
config.Saver
|
||||||
|
|
||||||
// Listen parameters for the IPFS Proxy.
|
// Listen parameters for the IPFS Proxy.
|
||||||
ListenAddr ma.Multiaddr
|
ListenAddr []ma.Multiaddr
|
||||||
|
|
||||||
// Host/Port for the IPFS daemon.
|
// Host/Port for the IPFS daemon.
|
||||||
NodeAddr ma.Multiaddr
|
NodeAddr ma.Multiaddr
|
||||||
|
@ -93,9 +98,9 @@ type Config struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type jsonConfig struct {
|
type jsonConfig struct {
|
||||||
ListenMultiaddress string `json:"listen_multiaddress"`
|
ListenMultiaddress ipfsconfig.Strings `json:"listen_multiaddress"`
|
||||||
NodeMultiaddress string `json:"node_multiaddress"`
|
NodeMultiaddress string `json:"node_multiaddress"`
|
||||||
NodeHTTPS bool `json:"node_https,omitempty"`
|
NodeHTTPS bool `json:"node_https,omitempty"`
|
||||||
|
|
||||||
LogFile string `json:"log_file"`
|
LogFile string `json:"log_file"`
|
||||||
|
|
||||||
|
@ -131,9 +136,13 @@ func (cfg *Config) ConfigKey() string {
|
||||||
|
|
||||||
// Default sets the fields of this Config to sensible default values.
|
// Default sets the fields of this Config to sensible default values.
|
||||||
func (cfg *Config) Default() error {
|
func (cfg *Config) Default() error {
|
||||||
proxy, err := ma.NewMultiaddr(DefaultListenAddr)
|
proxy := make([]ma.Multiaddr, 0, len(DefaultListenAddrs))
|
||||||
if err != nil {
|
for _, def := range DefaultListenAddrs {
|
||||||
return err
|
a, err := ma.NewMultiaddr(def)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
proxy = append(proxy, a)
|
||||||
}
|
}
|
||||||
node, err := ma.NewMultiaddr(DefaultNodeAddr)
|
node, err := ma.NewMultiaddr(DefaultNodeAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -174,7 +183,7 @@ func (cfg *Config) ApplyEnvVars() error {
|
||||||
// at least in appearance.
|
// at least in appearance.
|
||||||
func (cfg *Config) Validate() error {
|
func (cfg *Config) Validate() error {
|
||||||
var err error
|
var err error
|
||||||
if cfg.ListenAddr == nil {
|
if len(cfg.ListenAddr) == 0 {
|
||||||
err = errors.New("ipfsproxy.listen_multiaddress not set")
|
err = errors.New("ipfsproxy.listen_multiaddress not set")
|
||||||
}
|
}
|
||||||
if cfg.NodeAddr == nil {
|
if cfg.NodeAddr == nil {
|
||||||
|
@ -230,12 +239,15 @@ func (cfg *Config) LoadJSON(raw []byte) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *Config) applyJSONConfig(jcfg *jsonConfig) error {
|
func (cfg *Config) applyJSONConfig(jcfg *jsonConfig) error {
|
||||||
if jcfg.ListenMultiaddress != "" {
|
if addresses := jcfg.ListenMultiaddress; len(addresses) > 0 {
|
||||||
proxyAddr, err := ma.NewMultiaddr(jcfg.ListenMultiaddress)
|
cfg.ListenAddr = make([]ma.Multiaddr, 0, len(addresses))
|
||||||
if err != nil {
|
for _, a := range addresses {
|
||||||
return fmt.Errorf("error parsing proxy listen_multiaddress: %s", err)
|
proxyAddr, err := ma.NewMultiaddr(a)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error parsing proxy listen_multiaddress: %s", err)
|
||||||
|
}
|
||||||
|
cfg.ListenAddr = append(cfg.ListenAddr, proxyAddr)
|
||||||
}
|
}
|
||||||
cfg.ListenAddr = proxyAddr
|
|
||||||
}
|
}
|
||||||
if jcfg.NodeMultiaddress != "" {
|
if jcfg.NodeMultiaddress != "" {
|
||||||
nodeAddr, err := ma.NewMultiaddr(jcfg.NodeMultiaddress)
|
nodeAddr, err := ma.NewMultiaddr(jcfg.NodeMultiaddress)
|
||||||
|
@ -295,8 +307,13 @@ func (cfg *Config) toJSONConfig() (jcfg *jsonConfig, err error) {
|
||||||
|
|
||||||
jcfg = &jsonConfig{}
|
jcfg = &jsonConfig{}
|
||||||
|
|
||||||
|
addresses := make([]string, 0, len(cfg.ListenAddr))
|
||||||
|
for _, a := range cfg.ListenAddr {
|
||||||
|
addresses = append(addresses, a.String())
|
||||||
|
}
|
||||||
|
|
||||||
// Set all configuration fields
|
// Set all configuration fields
|
||||||
jcfg.ListenMultiaddress = cfg.ListenAddr.String()
|
jcfg.ListenMultiaddress = addresses
|
||||||
jcfg.NodeMultiaddress = cfg.NodeAddr.String()
|
jcfg.NodeMultiaddress = cfg.NodeAddr.String()
|
||||||
jcfg.ReadTimeout = cfg.ReadTimeout.String()
|
jcfg.ReadTimeout = cfg.ReadTimeout.String()
|
||||||
jcfg.ReadHeaderTimeout = cfg.ReadHeaderTimeout.String()
|
jcfg.ReadHeaderTimeout = cfg.ReadHeaderTimeout.String()
|
||||||
|
|
|
@ -40,7 +40,7 @@ func TestLoadJSON(t *testing.T) {
|
||||||
|
|
||||||
j := &jsonConfig{}
|
j := &jsonConfig{}
|
||||||
json.Unmarshal(cfgJSON, j)
|
json.Unmarshal(cfgJSON, j)
|
||||||
j.ListenMultiaddress = "abc"
|
j.ListenMultiaddress = []string{"abc"}
|
||||||
tst, _ := json.Marshal(j)
|
tst, _ := json.Marshal(j)
|
||||||
err = cfg.LoadJSON(tst)
|
err = cfg.LoadJSON(tst)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
|
|
@ -58,7 +58,7 @@ type Server struct {
|
||||||
rpcClient *rpc.Client
|
rpcClient *rpc.Client
|
||||||
rpcReady chan struct{}
|
rpcReady chan struct{}
|
||||||
|
|
||||||
listener net.Listener // proxy listener
|
listeners []net.Listener // proxy listener
|
||||||
server *http.Server // proxy server
|
server *http.Server // proxy server
|
||||||
ipfsRoundTripper http.RoundTripper // allows to talk to IPFS
|
ipfsRoundTripper http.RoundTripper // allows to talk to IPFS
|
||||||
|
|
||||||
|
@ -126,14 +126,18 @@ func New(cfg *Config) (*Server, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
proxyNet, proxyAddr, err := manet.DialArgs(cfg.ListenAddr)
|
var listeners []net.Listener
|
||||||
if err != nil {
|
for _, addr := range cfg.ListenAddr {
|
||||||
return nil, err
|
proxyNet, proxyAddr, err := manet.DialArgs(addr)
|
||||||
}
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
l, err := net.Listen(proxyNet, proxyAddr)
|
l, err := net.Listen(proxyNet, proxyAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
}
|
||||||
|
listeners = append(listeners, l)
|
||||||
}
|
}
|
||||||
|
|
||||||
nodeScheme := "http"
|
nodeScheme := "http"
|
||||||
|
@ -197,7 +201,7 @@ func New(cfg *Config) (*Server, error) {
|
||||||
nodeAddr: nodeHTTPAddr,
|
nodeAddr: nodeHTTPAddr,
|
||||||
nodeScheme: nodeScheme,
|
nodeScheme: nodeScheme,
|
||||||
rpcReady: make(chan struct{}, 1),
|
rpcReady: make(chan struct{}, 1),
|
||||||
listener: l,
|
listeners: listeners,
|
||||||
server: s,
|
server: s,
|
||||||
ipfsRoundTripper: reverseProxy.Transport,
|
ipfsRoundTripper: reverseProxy.Transport,
|
||||||
}
|
}
|
||||||
|
@ -284,7 +288,9 @@ func (proxy *Server) Shutdown(ctx context.Context) error {
|
||||||
proxy.cancel()
|
proxy.cancel()
|
||||||
close(proxy.rpcReady)
|
close(proxy.rpcReady)
|
||||||
proxy.server.SetKeepAlivesEnabled(false)
|
proxy.server.SetKeepAlivesEnabled(false)
|
||||||
proxy.listener.Close()
|
for _, l := range proxy.listeners {
|
||||||
|
l.Close()
|
||||||
|
}
|
||||||
|
|
||||||
proxy.wg.Wait()
|
proxy.wg.Wait()
|
||||||
proxy.shutdown = true
|
proxy.shutdown = true
|
||||||
|
@ -301,19 +307,27 @@ func (proxy *Server) run() {
|
||||||
defer proxy.shutdownLock.Unlock()
|
defer proxy.shutdownLock.Unlock()
|
||||||
|
|
||||||
// This launches the proxy
|
// This launches the proxy
|
||||||
proxy.wg.Add(1)
|
proxy.wg.Add(len(proxy.listeners))
|
||||||
go func() {
|
for _, l := range proxy.listeners {
|
||||||
defer proxy.wg.Done()
|
go func(l net.Listener) {
|
||||||
logger.Infof(
|
defer proxy.wg.Done()
|
||||||
"IPFS Proxy: %s -> %s",
|
|
||||||
proxy.config.ListenAddr,
|
maddr, err := manet.FromNetAddr(l.Addr())
|
||||||
proxy.config.NodeAddr,
|
if err != nil {
|
||||||
)
|
logger.Error(err)
|
||||||
err := proxy.server.Serve(proxy.listener) // hangs here
|
}
|
||||||
if err != nil && !strings.Contains(err.Error(), "closed network connection") {
|
|
||||||
logger.Error(err)
|
logger.Infof(
|
||||||
}
|
"IPFS Proxy: %s -> %s",
|
||||||
}()
|
maddr,
|
||||||
|
proxy.config.NodeAddr,
|
||||||
|
)
|
||||||
|
err = proxy.server.Serve(l) // hangs here
|
||||||
|
if err != nil && !strings.Contains(err.Error(), "closed network connection") {
|
||||||
|
logger.Error(err)
|
||||||
|
}
|
||||||
|
}(l)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ipfsErrorResponder writes an http error response just like IPFS would.
|
// ipfsErrorResponder writes an http error response just like IPFS would.
|
||||||
|
|
|
@ -34,7 +34,7 @@ func testIPFSProxyWithConfig(t *testing.T, cfg *Config) (*Server, *test.IpfsMock
|
||||||
proxyMAddr, _ := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/0")
|
proxyMAddr, _ := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/0")
|
||||||
|
|
||||||
cfg.NodeAddr = nodeMAddr
|
cfg.NodeAddr = nodeMAddr
|
||||||
cfg.ListenAddr = proxyMAddr
|
cfg.ListenAddr = []ma.Multiaddr{proxyMAddr}
|
||||||
cfg.ExtractHeadersExtra = []string{
|
cfg.ExtractHeadersExtra = []string{
|
||||||
test.IpfsCustomHeaderName,
|
test.IpfsCustomHeaderName,
|
||||||
test.IpfsTimeHeaderName,
|
test.IpfsTimeHeaderName,
|
||||||
|
@ -716,7 +716,7 @@ func TestProxyError(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func proxyURL(c *Server) string {
|
func proxyURL(c *Server) string {
|
||||||
addr := c.listener.Addr()
|
addr := c.listeners[0].Addr()
|
||||||
return fmt.Sprintf("http://%s/api/v0", addr.String())
|
return fmt.Sprintf("http://%s/api/v0", addr.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ func testAPI(t *testing.T) *rest.API {
|
||||||
|
|
||||||
cfg := &rest.Config{}
|
cfg := &rest.Config{}
|
||||||
cfg.Default()
|
cfg.Default()
|
||||||
cfg.HTTPListenAddr = apiMAddr
|
cfg.HTTPListenAddr = []ma.Multiaddr{apiMAddr}
|
||||||
var secret [32]byte
|
var secret [32]byte
|
||||||
prot, err := pnet.NewV1ProtectorFromBytes(&secret)
|
prot, err := pnet.NewV1ProtectorFromBytes(&secret)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -54,8 +54,8 @@ func shutdown(a *rest.API) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func apiMAddr(a *rest.API) ma.Multiaddr {
|
func apiMAddr(a *rest.API) ma.Multiaddr {
|
||||||
listen, _ := a.HTTPAddress()
|
listen, _ := a.HTTPAddresses()
|
||||||
hostPort := strings.Split(listen, ":")
|
hostPort := strings.Split(listen[0], ":")
|
||||||
|
|
||||||
addr, _ := ma.NewMultiaddr(fmt.Sprintf("/ip4/127.0.0.1/tcp/%s", hostPort[1]))
|
addr, _ := ma.NewMultiaddr(fmt.Sprintf("/ip4/127.0.0.1/tcp/%s", hostPort[1]))
|
||||||
return addr
|
return addr
|
||||||
|
|
|
@ -10,14 +10,15 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ipfs/ipfs-cluster/config"
|
ipfsconfig "github.com/ipfs/go-ipfs-config"
|
||||||
|
|
||||||
crypto "github.com/libp2p/go-libp2p-core/crypto"
|
crypto "github.com/libp2p/go-libp2p-core/crypto"
|
||||||
peer "github.com/libp2p/go-libp2p-core/peer"
|
peer "github.com/libp2p/go-libp2p-core/peer"
|
||||||
ma "github.com/multiformats/go-multiaddr"
|
ma "github.com/multiformats/go-multiaddr"
|
||||||
|
|
||||||
"github.com/kelseyhightower/envconfig"
|
"github.com/kelseyhightower/envconfig"
|
||||||
"github.com/rs/cors"
|
"github.com/rs/cors"
|
||||||
|
|
||||||
|
"github.com/ipfs/ipfs-cluster/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
const configKey = "restapi"
|
const configKey = "restapi"
|
||||||
|
@ -25,9 +26,13 @@ const envConfigKey = "cluster_restapi"
|
||||||
|
|
||||||
const minMaxHeaderBytes = 4096
|
const minMaxHeaderBytes = 4096
|
||||||
|
|
||||||
|
// DefaultHTTPListenAddrs contains default listen addresses for the HTTP API.
|
||||||
|
var DefaultHTTPListenAddrs = []string{
|
||||||
|
"/ip4/127.0.0.1/tcp/9094",
|
||||||
|
}
|
||||||
|
|
||||||
// These are the default values for Config
|
// These are the default values for Config
|
||||||
const (
|
const (
|
||||||
DefaultHTTPListenAddr = "/ip4/127.0.0.1/tcp/9094"
|
|
||||||
DefaultReadTimeout = 0
|
DefaultReadTimeout = 0
|
||||||
DefaultReadHeaderTimeout = 5 * time.Second
|
DefaultReadHeaderTimeout = 5 * time.Second
|
||||||
DefaultWriteTimeout = 0
|
DefaultWriteTimeout = 0
|
||||||
|
@ -66,7 +71,7 @@ type Config struct {
|
||||||
config.Saver
|
config.Saver
|
||||||
|
|
||||||
// Listen address for the HTTP REST API endpoint.
|
// Listen address for the HTTP REST API endpoint.
|
||||||
HTTPListenAddr ma.Multiaddr
|
HTTPListenAddr []ma.Multiaddr
|
||||||
|
|
||||||
// TLS configuration for the HTTP listener
|
// TLS configuration for the HTTP listener
|
||||||
TLS *tls.Config
|
TLS *tls.Config
|
||||||
|
@ -97,7 +102,7 @@ type Config struct {
|
||||||
MaxHeaderBytes int
|
MaxHeaderBytes int
|
||||||
|
|
||||||
// Listen address for the Libp2p REST API endpoint.
|
// Listen address for the Libp2p REST API endpoint.
|
||||||
Libp2pListenAddr ma.Multiaddr
|
Libp2pListenAddr []ma.Multiaddr
|
||||||
|
|
||||||
// ID and PrivateKey are used to create a libp2p host if we
|
// ID and PrivateKey are used to create a libp2p host if we
|
||||||
// want the API component to do it (not by default).
|
// want the API component to do it (not by default).
|
||||||
|
@ -131,18 +136,18 @@ type Config struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type jsonConfig struct {
|
type jsonConfig struct {
|
||||||
HTTPListenMultiaddress string `json:"http_listen_multiaddress"`
|
HTTPListenMultiaddress ipfsconfig.Strings `json:"http_listen_multiaddress"`
|
||||||
SSLCertFile string `json:"ssl_cert_file,omitempty"`
|
SSLCertFile string `json:"ssl_cert_file,omitempty"`
|
||||||
SSLKeyFile string `json:"ssl_key_file,omitempty"`
|
SSLKeyFile string `json:"ssl_key_file,omitempty"`
|
||||||
ReadTimeout string `json:"read_timeout"`
|
ReadTimeout string `json:"read_timeout"`
|
||||||
ReadHeaderTimeout string `json:"read_header_timeout"`
|
ReadHeaderTimeout string `json:"read_header_timeout"`
|
||||||
WriteTimeout string `json:"write_timeout"`
|
WriteTimeout string `json:"write_timeout"`
|
||||||
IdleTimeout string `json:"idle_timeout"`
|
IdleTimeout string `json:"idle_timeout"`
|
||||||
MaxHeaderBytes int `json:"max_header_bytes"`
|
MaxHeaderBytes int `json:"max_header_bytes"`
|
||||||
|
|
||||||
Libp2pListenMultiaddress string `json:"libp2p_listen_multiaddress,omitempty"`
|
Libp2pListenMultiaddress ipfsconfig.Strings `json:"libp2p_listen_multiaddress,omitempty"`
|
||||||
ID string `json:"id,omitempty"`
|
ID string `json:"id,omitempty"`
|
||||||
PrivateKey string `json:"private_key,omitempty"`
|
PrivateKey string `json:"private_key,omitempty"`
|
||||||
|
|
||||||
BasicAuthCredentials map[string]string `json:"basic_auth_credentials"`
|
BasicAuthCredentials map[string]string `json:"basic_auth_credentials"`
|
||||||
HTTPLogFile string `json:"http_log_file"`
|
HTTPLogFile string `json:"http_log_file"`
|
||||||
|
@ -179,8 +184,15 @@ func (cfg *Config) ConfigKey() string {
|
||||||
// Default initializes this Config with working values.
|
// Default initializes this Config with working values.
|
||||||
func (cfg *Config) Default() error {
|
func (cfg *Config) Default() error {
|
||||||
// http
|
// http
|
||||||
httpListen, _ := ma.NewMultiaddr(DefaultHTTPListenAddr)
|
addrs := make([]ma.Multiaddr, 0, len(DefaultHTTPListenAddrs))
|
||||||
cfg.HTTPListenAddr = httpListen
|
for _, def := range DefaultHTTPListenAddrs {
|
||||||
|
httpListen, err := ma.NewMultiaddr(def)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
addrs = append(addrs, httpListen)
|
||||||
|
}
|
||||||
|
cfg.HTTPListenAddr = addrs
|
||||||
cfg.pathSSLCertFile = ""
|
cfg.pathSSLCertFile = ""
|
||||||
cfg.pathSSLKeyFile = ""
|
cfg.pathSSLKeyFile = ""
|
||||||
cfg.ReadTimeout = DefaultReadTimeout
|
cfg.ReadTimeout = DefaultReadTimeout
|
||||||
|
@ -225,7 +237,6 @@ func (cfg *Config) ApplyEnvVars() error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return cfg.applyJSONConfig(jcfg)
|
return cfg.applyJSONConfig(jcfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -255,9 +266,9 @@ func (cfg *Config) Validate() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *Config) validateLibp2p() error {
|
func (cfg *Config) validateLibp2p() error {
|
||||||
if cfg.ID != "" || cfg.PrivateKey != nil || cfg.Libp2pListenAddr != nil {
|
if cfg.ID != "" || cfg.PrivateKey != nil || len(cfg.Libp2pListenAddr) > 0 {
|
||||||
// if one is set, all should be
|
// if one is set, all should be
|
||||||
if cfg.ID == "" || cfg.PrivateKey == nil || cfg.Libp2pListenAddr == nil {
|
if cfg.ID == "" || cfg.PrivateKey == nil || len(cfg.Libp2pListenAddr) == 0 {
|
||||||
return errors.New("all ID, private_key and libp2p_listen_multiaddress should be set")
|
return errors.New("all ID, private_key and libp2p_listen_multiaddress should be set")
|
||||||
}
|
}
|
||||||
if !cfg.ID.MatchesPrivateKey(cfg.PrivateKey) {
|
if !cfg.ID.MatchesPrivateKey(cfg.PrivateKey) {
|
||||||
|
@ -288,6 +299,7 @@ func (cfg *Config) applyJSONConfig(jcfg *jsonConfig) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = cfg.loadLibp2pOptions(jcfg)
|
err = cfg.loadLibp2pOptions(jcfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -302,13 +314,16 @@ func (cfg *Config) applyJSONConfig(jcfg *jsonConfig) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *Config) loadHTTPOptions(jcfg *jsonConfig) error {
|
func (cfg *Config) loadHTTPOptions(jcfg *jsonConfig) error {
|
||||||
if httpListen := jcfg.HTTPListenMultiaddress; httpListen != "" {
|
if addresses := jcfg.HTTPListenMultiaddress; len(addresses) > 0 {
|
||||||
httpAddr, err := ma.NewMultiaddr(httpListen)
|
cfg.HTTPListenAddr = make([]ma.Multiaddr, 0, len(addresses))
|
||||||
if err != nil {
|
for _, addr := range addresses {
|
||||||
err = fmt.Errorf("error parsing restapi.http_listen_multiaddress: %s", err)
|
httpAddr, err := ma.NewMultiaddr(addr)
|
||||||
return err
|
if err != nil {
|
||||||
|
err = fmt.Errorf("error parsing restapi.http_listen_multiaddress: %s", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cfg.HTTPListenAddr = append(cfg.HTTPListenAddr, httpAddr)
|
||||||
}
|
}
|
||||||
cfg.HTTPListenAddr = httpAddr
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err := cfg.tlsOptions(jcfg)
|
err := cfg.tlsOptions(jcfg)
|
||||||
|
@ -373,13 +388,16 @@ func (cfg *Config) tlsOptions(jcfg *jsonConfig) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *Config) loadLibp2pOptions(jcfg *jsonConfig) error {
|
func (cfg *Config) loadLibp2pOptions(jcfg *jsonConfig) error {
|
||||||
if libp2pListen := jcfg.Libp2pListenMultiaddress; libp2pListen != "" {
|
if addresses := jcfg.Libp2pListenMultiaddress; len(addresses) > 0 {
|
||||||
libp2pAddr, err := ma.NewMultiaddr(libp2pListen)
|
cfg.Libp2pListenAddr = make([]ma.Multiaddr, 0, len(addresses))
|
||||||
if err != nil {
|
for _, addr := range addresses {
|
||||||
err = fmt.Errorf("error parsing restapi.libp2p_listen_multiaddress: %s", err)
|
libp2pAddr, err := ma.NewMultiaddr(addr)
|
||||||
return err
|
if err != nil {
|
||||||
|
err = fmt.Errorf("error parsing restapi.libp2p_listen_multiaddress: %s", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cfg.Libp2pListenAddr = append(cfg.Libp2pListenAddr, libp2pAddr)
|
||||||
}
|
}
|
||||||
cfg.Libp2pListenAddr = libp2pAddr
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if jcfg.PrivateKey != "" {
|
if jcfg.PrivateKey != "" {
|
||||||
|
@ -424,8 +442,18 @@ func (cfg *Config) toJSONConfig() (jcfg *jsonConfig, err error) {
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
httpAddresses := make([]string, 0, len(cfg.HTTPListenAddr))
|
||||||
|
for _, addr := range cfg.HTTPListenAddr {
|
||||||
|
httpAddresses = append(httpAddresses, addr.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
libp2pAddresses := make([]string, 0, len(cfg.Libp2pListenAddr))
|
||||||
|
for _, addr := range cfg.Libp2pListenAddr {
|
||||||
|
libp2pAddresses = append(libp2pAddresses, addr.String())
|
||||||
|
}
|
||||||
|
|
||||||
jcfg = &jsonConfig{
|
jcfg = &jsonConfig{
|
||||||
HTTPListenMultiaddress: cfg.HTTPListenAddr.String(),
|
HTTPListenMultiaddress: httpAddresses,
|
||||||
SSLCertFile: cfg.pathSSLCertFile,
|
SSLCertFile: cfg.pathSSLCertFile,
|
||||||
SSLKeyFile: cfg.pathSSLKeyFile,
|
SSLKeyFile: cfg.pathSSLKeyFile,
|
||||||
ReadTimeout: cfg.ReadTimeout.String(),
|
ReadTimeout: cfg.ReadTimeout.String(),
|
||||||
|
@ -454,8 +482,8 @@ func (cfg *Config) toJSONConfig() (jcfg *jsonConfig, err error) {
|
||||||
jcfg.PrivateKey = pKey
|
jcfg.PrivateKey = pKey
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if cfg.Libp2pListenAddr != nil {
|
if len(libp2pAddresses) > 0 {
|
||||||
jcfg.Libp2pListenMultiaddress = cfg.Libp2pListenAddr.String()
|
jcfg.Libp2pListenMultiaddress = libp2pAddresses
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
|
@ -58,7 +58,7 @@ func TestLoadJSON(t *testing.T) {
|
||||||
j := &jsonConfig{}
|
j := &jsonConfig{}
|
||||||
|
|
||||||
json.Unmarshal(cfgJSON, j)
|
json.Unmarshal(cfgJSON, j)
|
||||||
j.HTTPListenMultiaddress = "abc"
|
j.HTTPListenMultiaddress = []string{"abc"}
|
||||||
tst, _ := json.Marshal(j)
|
tst, _ := json.Marshal(j)
|
||||||
err = cfg.LoadJSON(tst)
|
err = cfg.LoadJSON(tst)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
@ -103,7 +103,7 @@ func TestLoadJSON(t *testing.T) {
|
||||||
|
|
||||||
j = &jsonConfig{}
|
j = &jsonConfig{}
|
||||||
json.Unmarshal(cfgJSON, j)
|
json.Unmarshal(cfgJSON, j)
|
||||||
j.Libp2pListenMultiaddress = "abc"
|
j.Libp2pListenMultiaddress = []string{"abc"}
|
||||||
tst, _ = json.Marshal(j)
|
tst, _ = json.Marshal(j)
|
||||||
err = cfg.LoadJSON(tst)
|
err = cfg.LoadJSON(tst)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
@ -178,8 +178,8 @@ func TestLibp2pConfig(t *testing.T) {
|
||||||
cfg.ID = pid
|
cfg.ID = pid
|
||||||
cfg.PrivateKey = priv
|
cfg.PrivateKey = priv
|
||||||
addr, _ := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/0")
|
addr, _ := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/0")
|
||||||
cfg.HTTPListenAddr = addr
|
cfg.HTTPListenAddr = []ma.Multiaddr{addr}
|
||||||
cfg.Libp2pListenAddr = addr
|
cfg.Libp2pListenAddr = []ma.Multiaddr{addr}
|
||||||
|
|
||||||
err = cfg.Validate()
|
err = cfg.Validate()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -38,7 +38,6 @@ import (
|
||||||
libp2pquic "github.com/libp2p/go-libp2p-quic-transport"
|
libp2pquic "github.com/libp2p/go-libp2p-quic-transport"
|
||||||
secio "github.com/libp2p/go-libp2p-secio"
|
secio "github.com/libp2p/go-libp2p-secio"
|
||||||
libp2ptls "github.com/libp2p/go-libp2p-tls"
|
libp2ptls "github.com/libp2p/go-libp2p-tls"
|
||||||
ma "github.com/multiformats/go-multiaddr"
|
|
||||||
manet "github.com/multiformats/go-multiaddr-net"
|
manet "github.com/multiformats/go-multiaddr-net"
|
||||||
|
|
||||||
handlers "github.com/gorilla/handlers"
|
handlers "github.com/gorilla/handlers"
|
||||||
|
@ -91,7 +90,7 @@ type API struct {
|
||||||
server *http.Server
|
server *http.Server
|
||||||
host host.Host
|
host host.Host
|
||||||
|
|
||||||
httpListener net.Listener
|
httpListeners []net.Listener
|
||||||
libp2pListener net.Listener
|
libp2pListener net.Listener
|
||||||
|
|
||||||
shutdownLock sync.Mutex
|
shutdownLock sync.Mutex
|
||||||
|
@ -187,19 +186,19 @@ func NewAPIWithHost(ctx context.Context, cfg *Config, h host.Host) (*API, error)
|
||||||
}
|
}
|
||||||
api.addRoutes(router)
|
api.addRoutes(router)
|
||||||
|
|
||||||
// Set up api.httpListener if enabled
|
// Set up api.httpListeners if enabled
|
||||||
err = api.setupHTTP()
|
err = api.setupHTTP()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up api.libp2pListener if enabled
|
// Set up api.libp2pListeners if enabled
|
||||||
err = api.setupLibp2p()
|
err = api.setupLibp2p()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if api.httpListener == nil && api.libp2pListener == nil {
|
if len(api.httpListeners) == 0 && api.libp2pListener == nil {
|
||||||
return nil, ErrNoEndpointsEnabled
|
return nil, ErrNoEndpointsEnabled
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -208,39 +207,41 @@ func NewAPIWithHost(ctx context.Context, cfg *Config, h host.Host) (*API, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *API) setupHTTP() error {
|
func (api *API) setupHTTP() error {
|
||||||
if api.config.HTTPListenAddr == nil {
|
if len(api.config.HTTPListenAddr) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
n, addr, err := manet.DialArgs(api.config.HTTPListenAddr)
|
for _, listenMAddr := range api.config.HTTPListenAddr {
|
||||||
if err != nil {
|
n, addr, err := manet.DialArgs(listenMAddr)
|
||||||
return err
|
if err != nil {
|
||||||
}
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
var l net.Listener
|
var l net.Listener
|
||||||
if api.config.TLS != nil {
|
if api.config.TLS != nil {
|
||||||
l, err = tls.Listen(n, addr, api.config.TLS)
|
l, err = tls.Listen(n, addr, api.config.TLS)
|
||||||
} else {
|
} else {
|
||||||
l, err = net.Listen(n, addr)
|
l, err = net.Listen(n, addr)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
api.httpListeners = append(api.httpListeners, l)
|
||||||
}
|
}
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
api.httpListener = l
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *API) setupLibp2p() error {
|
func (api *API) setupLibp2p() error {
|
||||||
// Make new host. Override any provided existing one
|
// Make new host. Override any provided existing one
|
||||||
// if we have config for a custom one.
|
// if we have config for a custom one.
|
||||||
if api.config.Libp2pListenAddr != nil {
|
if len(api.config.Libp2pListenAddr) > 0 {
|
||||||
// We use a new host context. We will call
|
// We use a new host context. We will call
|
||||||
// Close() on shutdown(). Avoids things like:
|
// Close() on shutdown(). Avoids things like:
|
||||||
// https://github.com/ipfs/ipfs-cluster/issues/853
|
// https://github.com/ipfs/ipfs-cluster/issues/853
|
||||||
h, err := libp2p.New(
|
h, err := libp2p.New(
|
||||||
context.Background(),
|
context.Background(),
|
||||||
libp2p.Identity(api.config.PrivateKey),
|
libp2p.Identity(api.config.PrivateKey),
|
||||||
libp2p.ListenAddrs([]ma.Multiaddr{api.config.Libp2pListenAddr}...),
|
libp2p.ListenAddrs(api.config.Libp2pListenAddr...),
|
||||||
libp2p.Security(libp2ptls.ID, libp2ptls.New),
|
libp2p.Security(libp2ptls.ID, libp2ptls.New),
|
||||||
libp2p.Security(secio.ID, secio.New),
|
libp2p.Security(secio.ID, secio.New),
|
||||||
libp2p.Transport(libp2pquic.NewTransport),
|
libp2p.Transport(libp2pquic.NewTransport),
|
||||||
|
@ -264,15 +265,20 @@ func (api *API) setupLibp2p() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// HTTPAddress returns the HTTP(s) listening address
|
// HTTPAddresses returns the HTTP(s) listening address
|
||||||
// in host:port format. Useful when configured to start
|
// in host:port format. Useful when configured to start
|
||||||
// on a random port (0). Returns error when the HTTP endpoint
|
// on a random port (0). Returns error when the HTTP endpoint
|
||||||
// is not enabled.
|
// is not enabled.
|
||||||
func (api *API) HTTPAddress() (string, error) {
|
func (api *API) HTTPAddresses() ([]string, error) {
|
||||||
if api.httpListener == nil {
|
if len(api.httpListeners) == 0 {
|
||||||
return "", ErrHTTPEndpointNotEnabled
|
return nil, ErrHTTPEndpointNotEnabled
|
||||||
}
|
}
|
||||||
return api.httpListener.Addr().String(), nil
|
var addrs []string
|
||||||
|
for _, l := range api.httpListeners {
|
||||||
|
addrs = append(addrs, l.Addr().String())
|
||||||
|
}
|
||||||
|
|
||||||
|
return addrs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Host returns the libp2p Host used by the API, if any.
|
// Host returns the libp2p Host used by the API, if any.
|
||||||
|
@ -479,28 +485,38 @@ func (api *API) routes() []route {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *API) run(ctx context.Context) {
|
func (api *API) run(ctx context.Context) {
|
||||||
if api.httpListener != nil {
|
api.wg.Add(len(api.httpListeners))
|
||||||
api.wg.Add(1)
|
for _, l := range api.httpListeners {
|
||||||
go api.runHTTPServer(ctx)
|
go func(l net.Listener) {
|
||||||
|
defer api.wg.Done()
|
||||||
|
api.runHTTPServer(ctx, l)
|
||||||
|
}(l)
|
||||||
}
|
}
|
||||||
|
|
||||||
if api.libp2pListener != nil {
|
if api.libp2pListener != nil {
|
||||||
api.wg.Add(1)
|
api.wg.Add(1)
|
||||||
go api.runLibp2pServer(ctx)
|
go func() {
|
||||||
|
defer api.wg.Done()
|
||||||
|
api.runLibp2pServer(ctx)
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// runs in goroutine from run()
|
// runs in goroutine from run()
|
||||||
func (api *API) runHTTPServer(ctx context.Context) {
|
func (api *API) runHTTPServer(ctx context.Context, l net.Listener) {
|
||||||
defer api.wg.Done()
|
|
||||||
select {
|
select {
|
||||||
case <-api.rpcReady:
|
case <-api.rpcReady:
|
||||||
case <-api.ctx.Done():
|
case <-api.ctx.Done():
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Infof("REST API (HTTP): %s", api.config.HTTPListenAddr)
|
maddr, err := manet.FromNetAddr(l.Addr())
|
||||||
err := api.server.Serve(api.httpListener)
|
if err != nil {
|
||||||
|
logger.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Infof("REST API (HTTP): %s", maddr)
|
||||||
|
err = api.server.Serve(l)
|
||||||
if err != nil && !strings.Contains(err.Error(), "closed network connection") {
|
if err != nil && !strings.Contains(err.Error(), "closed network connection") {
|
||||||
logger.Error(err)
|
logger.Error(err)
|
||||||
}
|
}
|
||||||
|
@ -508,8 +524,6 @@ func (api *API) runHTTPServer(ctx context.Context) {
|
||||||
|
|
||||||
// runs in goroutine from run()
|
// runs in goroutine from run()
|
||||||
func (api *API) runLibp2pServer(ctx context.Context) {
|
func (api *API) runLibp2pServer(ctx context.Context) {
|
||||||
defer api.wg.Done()
|
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-api.rpcReady:
|
case <-api.rpcReady:
|
||||||
case <-api.ctx.Done():
|
case <-api.ctx.Done():
|
||||||
|
@ -550,9 +564,10 @@ func (api *API) Shutdown(ctx context.Context) error {
|
||||||
// Cancel any outstanding ops
|
// Cancel any outstanding ops
|
||||||
api.server.SetKeepAlivesEnabled(false)
|
api.server.SetKeepAlivesEnabled(false)
|
||||||
|
|
||||||
if api.httpListener != nil {
|
for _, l := range api.httpListeners {
|
||||||
api.httpListener.Close()
|
l.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
if api.libp2pListener != nil {
|
if api.libp2pListener != nil {
|
||||||
api.libp2pListener.Close()
|
api.libp2pListener.Close()
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,7 +50,7 @@ func testAPIwithConfig(t *testing.T, cfg *Config, name string) *API {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg.HTTPListenAddr = apiMAddr
|
cfg.HTTPListenAddr = []ma.Multiaddr{apiMAddr}
|
||||||
|
|
||||||
rest, err := NewAPIWithHost(ctx, cfg, h)
|
rest, err := NewAPIWithHost(ctx, cfg, h)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -174,8 +174,8 @@ func makeHost(t *testing.T, rest *API) host.Host {
|
||||||
type urlF func(a *API) string
|
type urlF func(a *API) string
|
||||||
|
|
||||||
func httpURL(a *API) string {
|
func httpURL(a *API) string {
|
||||||
u, _ := a.HTTPAddress()
|
u, _ := a.HTTPAddresses()
|
||||||
return fmt.Sprintf("http://%s", u)
|
return fmt.Sprintf("http://%s", u[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
func p2pURL(a *API) string {
|
func p2pURL(a *API) string {
|
||||||
|
@ -183,8 +183,8 @@ func p2pURL(a *API) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func httpsURL(a *API) string {
|
func httpsURL(a *API) string {
|
||||||
u, _ := a.HTTPAddress()
|
u, _ := a.HTTPAddresses()
|
||||||
return fmt.Sprintf("https://%s", u)
|
return fmt.Sprintf("https://%s", u[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
func isHTTPS(url string) bool {
|
func isHTTPS(url string) bool {
|
||||||
|
|
|
@ -22,8 +22,11 @@ import (
|
||||||
|
|
||||||
const configKey = "cluster"
|
const configKey = "cluster"
|
||||||
|
|
||||||
// DefaultListenAddrs contains TCP and QUIC listen addresses
|
// DefaultListenAddrs contains TCP and QUIC listen addresses.
|
||||||
var DefaultListenAddrs = []string{"/ip4/0.0.0.0/tcp/9096", "/ip4/0.0.0.0/udp/9096/quic"}
|
var DefaultListenAddrs = []string{
|
||||||
|
"/ip4/0.0.0.0/tcp/9096",
|
||||||
|
"/ip4/0.0.0.0/udp/9096/quic",
|
||||||
|
}
|
||||||
|
|
||||||
// Configuration defaults
|
// Configuration defaults
|
||||||
const (
|
const (
|
||||||
|
|
|
@ -22,6 +22,7 @@ import (
|
||||||
"github.com/ipfs/ipfs-cluster/monitor/pubsubmon"
|
"github.com/ipfs/ipfs-cluster/monitor/pubsubmon"
|
||||||
"github.com/ipfs/ipfs-cluster/observations"
|
"github.com/ipfs/ipfs-cluster/observations"
|
||||||
"github.com/ipfs/ipfs-cluster/pintracker/stateless"
|
"github.com/ipfs/ipfs-cluster/pintracker/stateless"
|
||||||
|
"github.com/multiformats/go-multiaddr"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
cli "github.com/urfave/cli/v2"
|
cli "github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
@ -301,7 +302,7 @@ func runCmd(c *cli.Context) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cli.Exit(err, 1)
|
return cli.Exit(err, 1)
|
||||||
}
|
}
|
||||||
apiCfg.HTTPListenAddr = listenSocket
|
apiCfg.HTTPListenAddr = []multiaddr.Multiaddr{listenSocket}
|
||||||
// Allow customization via env vars
|
// Allow customization via env vars
|
||||||
err = apiCfg.ApplyEnvVars()
|
err = apiCfg.ApplyEnvVars()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -328,12 +328,11 @@ the peer IDs in the given multiaddresses.
|
||||||
// Generate defaults for all registered components
|
// Generate defaults for all registered components
|
||||||
err := cfgHelper.Manager().Default()
|
err := cfgHelper.Manager().Default()
|
||||||
checkErr("generating default configuration", err)
|
checkErr("generating default configuration", err)
|
||||||
|
|
||||||
if c.Bool("randomports") {
|
if c.Bool("randomports") {
|
||||||
cfgs := cfgHelper.Configs()
|
cfgs := cfgHelper.Configs()
|
||||||
|
|
||||||
for i := range cfgs.Cluster.ListenAddr {
|
cfgs.Cluster.ListenAddr, err = cmdutils.RandomizePorts(cfgs.Cluster.ListenAddr)
|
||||||
cfgs.Cluster.ListenAddr[i], err = cmdutils.RandomizePorts(cfgs.Cluster.ListenAddr[i])
|
|
||||||
}
|
|
||||||
checkErr("randomizing ports", err)
|
checkErr("randomizing ports", err)
|
||||||
cfgs.Restapi.HTTPListenAddr, err = cmdutils.RandomizePorts(cfgs.Restapi.HTTPListenAddr)
|
cfgs.Restapi.HTTPListenAddr, err = cmdutils.RandomizePorts(cfgs.Restapi.HTTPListenAddr)
|
||||||
checkErr("randomizing ports", err)
|
checkErr("randomizing ports", err)
|
||||||
|
|
|
@ -9,25 +9,30 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestRandomPorts(t *testing.T) {
|
func TestRandomPorts(t *testing.T) {
|
||||||
|
port := "9096"
|
||||||
m1, _ := ma.NewMultiaddr("/ip4/0.0.0.0/tcp/9096")
|
m1, _ := ma.NewMultiaddr("/ip4/0.0.0.0/tcp/9096")
|
||||||
m2, _ := ma.NewMultiaddr("/ip4/0.0.0.0/tcp/9096")
|
m2, _ := ma.NewMultiaddr("/ip6/::/udp/9096")
|
||||||
|
|
||||||
m1, err := cmdutils.RandomizePorts(m1)
|
addresses, err := cmdutils.RandomizePorts([]ma.Multiaddr{m1, m2})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
v1, err := m1.ValueForProtocol(ma.P_TCP)
|
v1, err := addresses[0].ValueForProtocol(ma.P_TCP)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
v2, err := m2.ValueForProtocol(ma.P_TCP)
|
v2, err := addresses[1].ValueForProtocol(ma.P_UDP)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if v1 == v2 {
|
if v1 == port {
|
||||||
t.Error("expected different ports")
|
t.Error("expected different ipv4 ports")
|
||||||
|
}
|
||||||
|
|
||||||
|
if v2 == port {
|
||||||
|
t.Error("expected different ipv6 ports")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,9 +5,11 @@ package cmdutils
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -20,51 +22,79 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// RandomizePorts replaces TCP and UDP ports with random, but valid port
|
// RandomizePorts replaces TCP and UDP ports with random, but valid port
|
||||||
// values.
|
// values, on the given multiaddresses
|
||||||
func RandomizePorts(m ma.Multiaddr) (ma.Multiaddr, error) {
|
func RandomizePorts(addrs []ma.Multiaddr) ([]ma.Multiaddr, error) {
|
||||||
var prev string
|
results := make([]ma.Multiaddr, 0, len(addrs))
|
||||||
|
|
||||||
var err error
|
for _, m := range addrs {
|
||||||
components := []ma.Multiaddr{}
|
var prev string
|
||||||
ma.ForEach(m, func(c ma.Component) bool {
|
var err error
|
||||||
code := c.Protocol().Code
|
components := []ma.Multiaddr{}
|
||||||
|
ma.ForEach(m, func(c ma.Component) bool {
|
||||||
|
code := c.Protocol().Code
|
||||||
|
|
||||||
if code != ma.P_TCP && code != ma.P_UDP {
|
if code != ma.P_TCP && code != ma.P_UDP {
|
||||||
components = append(components, &c)
|
components = append(components, &c)
|
||||||
|
prev = c.Value()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
var ln io.Closer
|
||||||
|
var port int
|
||||||
|
|
||||||
|
ip := prev
|
||||||
|
if strings.Contains(ip, ":") { // ipv6 needs bracketing
|
||||||
|
ip = "[" + ip + "]"
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.Protocol().Code == ma.P_UDP {
|
||||||
|
ln, port, err = listenUDP(c.Protocol().Name, ip)
|
||||||
|
} else {
|
||||||
|
ln, port, err = listenTCP(c.Protocol().Name, ip)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
defer ln.Close()
|
||||||
|
|
||||||
|
var c1 *ma.Component
|
||||||
|
c1, err = ma.NewComponent(c.Protocol().Name, fmt.Sprintf("%d", port))
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
components = append(components, c1)
|
||||||
prev = c.Value()
|
prev = c.Value()
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
})
|
||||||
|
|
||||||
var ln net.Listener
|
|
||||||
ln, err = net.Listen(c.Protocol().Name, prev+":")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return results, err
|
||||||
}
|
}
|
||||||
defer ln.Close()
|
results = append(results, ma.Join(components...))
|
||||||
|
}
|
||||||
|
|
||||||
var c1 *ma.Component
|
return results, nil
|
||||||
c1, err = ma.NewComponent(c.Protocol().Name, fmt.Sprintf("%d", getPort(ln, code)))
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
components = append(components, c1)
|
|
||||||
prev = c.Value()
|
|
||||||
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
|
|
||||||
return ma.Join(components...), err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getPort(ln net.Listener, code int) int {
|
// returns the listener so it can be closed later and port
|
||||||
if code == ma.P_TCP {
|
func listenTCP(name, ip string) (io.Closer, int, error) {
|
||||||
return ln.Addr().(*net.TCPAddr).Port
|
ln, err := net.Listen(name, ip+":0")
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
if code == ma.P_UDP {
|
|
||||||
return ln.Addr().(*net.UDPAddr).Port
|
return ln, ln.Addr().(*net.TCPAddr).Port, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns the listener so it can be cloesd later and port
|
||||||
|
func listenUDP(name, ip string) (io.Closer, int, error) {
|
||||||
|
ln, err := net.ListenPacket(name, ip+":0")
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
return 0
|
|
||||||
|
return ln, ln.LocalAddr().(*net.UDPAddr).Port, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// HandleSignals orderly shuts down an IPFS Cluster peer
|
// HandleSignals orderly shuts down an IPFS Cluster peer
|
||||||
|
|
|
@ -181,9 +181,9 @@ func createComponents(
|
||||||
clusterCfg.LeaveOnShutdown = false
|
clusterCfg.LeaveOnShutdown = false
|
||||||
clusterCfg.SetBaseDir(filepath.Join(testsFolder, host.ID().Pretty()))
|
clusterCfg.SetBaseDir(filepath.Join(testsFolder, host.ID().Pretty()))
|
||||||
|
|
||||||
apiCfg.HTTPListenAddr = apiAddr
|
apiCfg.HTTPListenAddr = []ma.Multiaddr{apiAddr}
|
||||||
|
|
||||||
ipfsproxyCfg.ListenAddr = proxyAddr
|
ipfsproxyCfg.ListenAddr = []ma.Multiaddr{proxyAddr}
|
||||||
ipfsproxyCfg.NodeAddr = nodeAddr
|
ipfsproxyCfg.NodeAddr = nodeAddr
|
||||||
|
|
||||||
ipfshttpCfg.NodeAddr = nodeAddr
|
ipfshttpCfg.NodeAddr = nodeAddr
|
||||||
|
|
|
@ -56,8 +56,8 @@ func TestIPFSID(t *testing.T) {
|
||||||
if id.ID != test.PeerID1 {
|
if id.ID != test.PeerID1 {
|
||||||
t.Error("expected testPeerID")
|
t.Error("expected testPeerID")
|
||||||
}
|
}
|
||||||
if len(id.Addresses) != 1 {
|
if len(id.Addresses) != 2 {
|
||||||
t.Error("expected 1 address")
|
t.Error("expected 2 address")
|
||||||
}
|
}
|
||||||
if id.Error != "" {
|
if id.Error != "" {
|
||||||
t.Error("expected no error")
|
t.Error("expected no error")
|
||||||
|
|
|
@ -3,7 +3,10 @@
|
||||||
"peername": "testname",
|
"peername": "testname",
|
||||||
"secret": "84399cd0be811c2ca372d6ca473ffd73c09034f991c5e306fe9ada6c5fcfb641",
|
"secret": "84399cd0be811c2ca372d6ca473ffd73c09034f991c5e306fe9ada6c5fcfb641",
|
||||||
"leave_on_shutdown": false,
|
"leave_on_shutdown": false,
|
||||||
"listen_multiaddress": "/ip4/0.0.0.0/tcp/9096",
|
"listen_multiaddress": [
|
||||||
|
"/ip4/0.0.0.0/tcp/9096",
|
||||||
|
"/ip6/::/tcp/9096"
|
||||||
|
],
|
||||||
"state_sync_interval": "1m0s",
|
"state_sync_interval": "1m0s",
|
||||||
"replication_factor": -1,
|
"replication_factor": -1,
|
||||||
"monitor_ping_interval": "15s"
|
"monitor_ping_interval": "15s"
|
||||||
|
|
|
@ -3,7 +3,10 @@
|
||||||
"peername": "testname",
|
"peername": "testname",
|
||||||
"secret": "84399cd0be811c2ca372d6ca473ffd73c09034f991c5e306fe9ada6c5fcfb641",
|
"secret": "84399cd0be811c2ca372d6ca473ffd73c09034f991c5e306fe9ada6c5fcfb641",
|
||||||
"leave_on_shutdown": false,
|
"leave_on_shutdown": false,
|
||||||
"listen_multiaddress": "/ip4/0.0.0.0/tcp/9096",
|
"listen_multiaddress": [
|
||||||
|
"/ip4/0.0.0.0/tcp/9096",
|
||||||
|
"/ip6/::/tcp/9096"
|
||||||
|
],
|
||||||
"state_sync_interval": "1m0s",
|
"state_sync_interval": "1m0s",
|
||||||
"replication_factor": -1,
|
"replication_factor": -1,
|
||||||
"monitor_ping_interval": "15s"
|
"monitor_ping_interval": "15s"
|
||||||
|
|
|
@ -5,7 +5,10 @@
|
||||||
"private_key": "CAASqAkwggSkAgEAAoIBAQC/ZmfWDbwyI0nJdRxgHcTdEaBFQo8sky9E+OOvtwZa5WKoLdHyHOLWxCAdpIHUBbhxz5rkMEWLwPI6ykqLIJToMPO8lJbKVzphOjv4JwpiAPdmeSiYMKLjx5V8MpqU2rwj/Uf3sRL8Gg9/Tei3PZ8cftxN1rkQQeeaOtk0CBxUFZSHEsyut1fbgIeL7TAY+4vCmXW0DBr4wh9fnoES/YivOvSiN9rScgWg6N65LfkI78hzaOJ4Nok2S4vYFCxjTAI9NWFUbhP5eJIFzTU+bZuQZxOn2qsoyw8pNZwuF+JClA/RcgBcCvVZcDH2ueVq/zT++bGCN+EWsAEdvJqJ5bsjAgMBAAECggEAaGDUZ6t94mnUJ4UyQEh7v4OJP7wYkFqEAL0qjfzl/lPyBX1XbQ3Ltwul6AR6uMGV4JszARZCFwDWGLGRDWZrTmTDxyfRQ+9l6vfzFFVWGDQmtz+Dn9uGOWnyX5TJMDxJNec+hBmRHOKpaOd37dYxGz0jr19V9UO7piRJp1J1AHUCypUGv5x1IekioSCu5fEyc7dyWwnmITHBjD08st+bCcjrIUFeXSdJKC8SymYeXdaVE3xH3zVEISKnrfT7bhuKZY1iibZIlXbVLNpyX36LkYJOiCqsMum3u70LH0VvTypkqiDbD4S6qfJ4vvUakpmKpOPutikiP7jkSP+AkaO0AQKBgQDkTuhnDK6+Y0a/HgpHJisji0coO+g2gsIszargHk8nNY2AB8t+EUn7C+Qu8cmrem5V8EXcdxS6z7iAXpJmY1Xepnsz+JP7Q91Lgt3OoqK5EybzUXXKkmNCD65n70Xxn2fEFzm6+GJP3c/HymlDKU2KBCYIyuUeaREjT0Fu3v6tgQKBgQDWnXppJwn4LJHhzFOCeO4zomDJDbLTZCabdKZoFP9r+vtEHAnclDDKx4AYbomSqgERe+DX6HR/tPHRVizP63RYPf7al2mJmPzt1nTkoc1/q5hQoD+oE154dADsW1pUp7AQjwCtys4iq5S0qAwIDpuY8M8bOHwZ+QmBvHYAigJCowKBgQC3HH6TX/2rH463bE2MARXqXSPGJj45sigwrQfW1xhe9zm1LQtN4mn2mvP5nt1D1l82OA6gIzYSGtX8x10eF5/ggqAf78goZ6bOkHh76b8fNzgvQO97eGt5qYAVRjhP8azU/lfEGMEpE1s5/6LrRe41utwSg0C+YkBnlIKDfQDAgQKBgDoBTCF5hK9H1JHzuKpt5uubuo78ndWWnvyrNYKyEirsJddNwLiWcO2NqChyT8qNGkbQdX/Fex89F5KduPTlTYfAEc6g18xxxgK+UM+uj60vArbf6PSTb5gculcnha2VuPdwvx050Cb8uu9s7/uJfzKB+2f/B0O51ID1H+ubYWsDAoGBAKrwGKHyqFTHSPg3XuRA1FgDAoOsfzP9ZJvMEXUWyu/VxjNt+0mRlyGeZ5qb9UZG+K/In4FbC/ux2P/PucCUIbgy/XGPtPXVavMwNbx0MquAcU0FihKXP0CUpi8zwiYc42MF7n/SztQnismxigBMSuJEDurcXXazjfcSRTypduNn",
|
"private_key": "CAASqAkwggSkAgEAAoIBAQC/ZmfWDbwyI0nJdRxgHcTdEaBFQo8sky9E+OOvtwZa5WKoLdHyHOLWxCAdpIHUBbhxz5rkMEWLwPI6ykqLIJToMPO8lJbKVzphOjv4JwpiAPdmeSiYMKLjx5V8MpqU2rwj/Uf3sRL8Gg9/Tei3PZ8cftxN1rkQQeeaOtk0CBxUFZSHEsyut1fbgIeL7TAY+4vCmXW0DBr4wh9fnoES/YivOvSiN9rScgWg6N65LfkI78hzaOJ4Nok2S4vYFCxjTAI9NWFUbhP5eJIFzTU+bZuQZxOn2qsoyw8pNZwuF+JClA/RcgBcCvVZcDH2ueVq/zT++bGCN+EWsAEdvJqJ5bsjAgMBAAECggEAaGDUZ6t94mnUJ4UyQEh7v4OJP7wYkFqEAL0qjfzl/lPyBX1XbQ3Ltwul6AR6uMGV4JszARZCFwDWGLGRDWZrTmTDxyfRQ+9l6vfzFFVWGDQmtz+Dn9uGOWnyX5TJMDxJNec+hBmRHOKpaOd37dYxGz0jr19V9UO7piRJp1J1AHUCypUGv5x1IekioSCu5fEyc7dyWwnmITHBjD08st+bCcjrIUFeXSdJKC8SymYeXdaVE3xH3zVEISKnrfT7bhuKZY1iibZIlXbVLNpyX36LkYJOiCqsMum3u70LH0VvTypkqiDbD4S6qfJ4vvUakpmKpOPutikiP7jkSP+AkaO0AQKBgQDkTuhnDK6+Y0a/HgpHJisji0coO+g2gsIszargHk8nNY2AB8t+EUn7C+Qu8cmrem5V8EXcdxS6z7iAXpJmY1Xepnsz+JP7Q91Lgt3OoqK5EybzUXXKkmNCD65n70Xxn2fEFzm6+GJP3c/HymlDKU2KBCYIyuUeaREjT0Fu3v6tgQKBgQDWnXppJwn4LJHhzFOCeO4zomDJDbLTZCabdKZoFP9r+vtEHAnclDDKx4AYbomSqgERe+DX6HR/tPHRVizP63RYPf7al2mJmPzt1nTkoc1/q5hQoD+oE154dADsW1pUp7AQjwCtys4iq5S0qAwIDpuY8M8bOHwZ+QmBvHYAigJCowKBgQC3HH6TX/2rH463bE2MARXqXSPGJj45sigwrQfW1xhe9zm1LQtN4mn2mvP5nt1D1l82OA6gIzYSGtX8x10eF5/ggqAf78goZ6bOkHh76b8fNzgvQO97eGt5qYAVRjhP8azU/lfEGMEpE1s5/6LrRe41utwSg0C+YkBnlIKDfQDAgQKBgDoBTCF5hK9H1JHzuKpt5uubuo78ndWWnvyrNYKyEirsJddNwLiWcO2NqChyT8qNGkbQdX/Fex89F5KduPTlTYfAEc6g18xxxgK+UM+uj60vArbf6PSTb5gculcnha2VuPdwvx050Cb8uu9s7/uJfzKB+2f/B0O51ID1H+ubYWsDAoGBAKrwGKHyqFTHSPg3XuRA1FgDAoOsfzP9ZJvMEXUWyu/VxjNt+0mRlyGeZ5qb9UZG+K/In4FbC/ux2P/PucCUIbgy/XGPtPXVavMwNbx0MquAcU0FihKXP0CUpi8zwiYc42MF7n/SztQnismxigBMSuJEDurcXXazjfcSRTypduNn",
|
||||||
"secret": "84399cd0be811c2ca372d6ca473ffd73c09034f991c5e306fe9ada6c5fcfb641",
|
"secret": "84399cd0be811c2ca372d6ca473ffd73c09034f991c5e306fe9ada6c5fcfb641",
|
||||||
"leave_on_shutdown": false,
|
"leave_on_shutdown": false,
|
||||||
"listen_multiaddress": "/ip4/0.0.0.0/tcp/9096",
|
"listen_multiaddress": [
|
||||||
|
"/ip4/0.0.0.0/tcp/9096",
|
||||||
|
"/ip6/::/tcp/9096"
|
||||||
|
],
|
||||||
"state_sync_interval": "1m0s",
|
"state_sync_interval": "1m0s",
|
||||||
"replication_factor": -1,
|
"replication_factor": -1,
|
||||||
"monitor_ping_interval": "15s"
|
"monitor_ping_interval": "15s"
|
||||||
|
|
|
@ -180,6 +180,7 @@ func (m *IpfsMock) handler(w http.ResponseWriter, r *http.Request) {
|
||||||
ID: PeerID1.Pretty(),
|
ID: PeerID1.Pretty(),
|
||||||
Addresses: []string{
|
Addresses: []string{
|
||||||
"/ip4/0.0.0.0/tcp/1234",
|
"/ip4/0.0.0.0/tcp/1234",
|
||||||
|
"/ip6/::/tcp/1234",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
j, _ := json.Marshal(resp)
|
j, _ := json.Marshal(resp)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user