diff --git a/cmd/ipfs-cluster-service/main.go b/cmd/ipfs-cluster-service/main.go index 16996124..a0e36326 100644 --- a/cmd/ipfs-cluster-service/main.go +++ b/cmd/ipfs-cluster-service/main.go @@ -272,6 +272,10 @@ the peer IDs in the given multiaddresses. Name: "force, f", Usage: "overwrite configuration without prompting", }, + cli.BoolFlag{ + Name: "randomports", + Usage: "configure random ports to listen on instead of defaults", + }, }, Action: func(c *cli.Context) error { consensus := c.String("consensus") @@ -319,6 +323,16 @@ the peer IDs in the given multiaddresses. // Generate defaults for all registered components err := cfgHelper.Manager().Default() checkErr("generating default configuration", err) + if c.Bool("randomports") { + cfgs := cfgHelper.Configs() + + cfgs.Cluster.ListenAddr, err = cmdutils.RandomizePorts(cfgs.Cluster.ListenAddr) + checkErr("randomizing ports", err) + cfgs.Restapi.HTTPListenAddr, err = cmdutils.RandomizePorts(cfgs.Restapi.HTTPListenAddr) + checkErr("randomizing ports", err) + cfgs.Ipfsproxy.ListenAddr, err = cmdutils.RandomizePorts(cfgs.Ipfsproxy.ListenAddr) + checkErr("randomizing ports", err) + } err = cfgHelper.Manager().ApplyEnvVars() checkErr("applying environment variables to configuration", err) diff --git a/cmd/ipfs-cluster-service/main_test.go b/cmd/ipfs-cluster-service/main_test.go new file mode 100644 index 00000000..4dbad8ee --- /dev/null +++ b/cmd/ipfs-cluster-service/main_test.go @@ -0,0 +1,33 @@ +package main + +import ( + "testing" + + "github.com/ipfs/ipfs-cluster/cmdutils" + + ma "github.com/multiformats/go-multiaddr" +) + +func TestRandomPorts(t *testing.T) { + m1, _ := ma.NewMultiaddr("/ip4/0.0.0.0/tcp/9096") + m2, _ := ma.NewMultiaddr("/ip4/0.0.0.0/tcp/9096") + + m1, err := cmdutils.RandomizePorts(m1) + if err != nil { + t.Fatal(err) + } + + v1, err := m1.ValueForProtocol(ma.P_TCP) + if err != nil { + t.Fatal(err) + } + + v2, err := m2.ValueForProtocol(ma.P_TCP) + if err != nil { + t.Fatal(err) + } + + if v1 == v2 { + t.Error("expected different ports") + } +} diff --git a/cmdutils/cmdutils.go b/cmdutils/cmdutils.go index c75ed81f..11752851 100644 --- a/cmdutils/cmdutils.go +++ b/cmdutils/cmdutils.go @@ -1,3 +1,58 @@ // Package cmdutils contains utilities to facilitate building of command line // applications launching cluster peers. package cmdutils + +import ( + "fmt" + "net" + + ma "github.com/multiformats/go-multiaddr" +) + +// RandomizePorts replaces TCP and UDP ports with random, but valid port +// values. +func RandomizePorts(m ma.Multiaddr) (ma.Multiaddr, error) { + var prev string + + var err error + components := []ma.Multiaddr{} + ma.ForEach(m, func(c ma.Component) bool { + code := c.Protocol().Code + + if code != ma.P_TCP && code != ma.P_UDP { + components = append(components, &c) + prev = c.Value() + return true + } + + var ln net.Listener + ln, err = net.Listen(c.Protocol().Name, prev+":") + if err != nil { + return false + } + defer ln.Close() + + var c1 *ma.Component + 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 { + if code == ma.P_TCP { + return ln.Addr().(*net.TCPAddr).Port + } + if code == ma.P_UDP { + return ln.Addr().(*net.UDPAddr).Port + } + return 0 +}