Skip to content

Commit

Permalink
Merge pull request #9 from filecoin-project/feat/netcli
Browse files Browse the repository at this point in the history
Network CLI
  • Loading branch information
whyrusleeping authored Jul 9, 2019
2 parents 83d1df1 + 271c268 commit b843dcb
Show file tree
Hide file tree
Showing 15 changed files with 383 additions and 39 deletions.
5 changes: 3 additions & 2 deletions api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,10 @@ type API interface {

// network

// // peers
NetPeers(context.Context) ([]peer.AddrInfo, error) // TODO: check serialization
NetConnect(context.Context, peer.AddrInfo) error
NetAddrsListen(context.Context) (MultiaddrSlice, error)
// // ping
// // connect

// Struct

Expand Down
16 changes: 16 additions & 0 deletions api/struct.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,25 @@ type Struct struct {
Internal struct {
ID func(context.Context) (peer.ID, error)
Version func(context.Context) (Version, error)

NetPeers func(context.Context) ([]peer.AddrInfo, error)
NetConnect func(context.Context, peer.AddrInfo) error
NetAddrsListen func(context.Context) (MultiaddrSlice, error)
}
}

func (c *Struct) NetPeers(ctx context.Context) ([]peer.AddrInfo, error) {
return c.Internal.NetPeers(ctx)
}

func (c *Struct) NetConnect(ctx context.Context, p peer.AddrInfo) error {
return c.Internal.NetConnect(ctx, p)
}

func (c *Struct) NetAddrsListen(ctx context.Context) (MultiaddrSlice, error) {
return c.Internal.NetAddrsListen(ctx)
}

// ID implements API.ID
func (c *Struct) ID(ctx context.Context) (peer.ID, error) {
return c.Internal.ID(ctx)
Expand Down
28 changes: 28 additions & 0 deletions api/types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package api

import (
"encoding/json"
ma "github.com/multiformats/go-multiaddr"
)

// TODO: check if this exists anywhere else
type MultiaddrSlice []ma.Multiaddr

func (m *MultiaddrSlice) UnmarshalJSON(raw []byte) (err error) {
var temp []string
if err := json.Unmarshal(raw, &temp); err != nil {
return err
}

res := make([]ma.Multiaddr, len(temp))
for i, str := range temp {
res[i], err = ma.NewMultiaddr(str)
if err != nil {
return err
}
}
*m = res
return nil
}

var _ json.Unmarshaler = new(MultiaddrSlice)
40 changes: 39 additions & 1 deletion cli/cmd.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,48 @@
package cli

import (
"context"
"os"
"os/signal"
"syscall"

"github.com/filecoin-project/go-lotus/api"
"gopkg.in/urfave/cli.v2"
)

// Commands is the root group of CLI commands
const (
metadataContext = "context"
metadataAPI = "api"
)

// ApiConnector returns API instance
type ApiConnector func() api.API

func getApi(ctx *cli.Context) api.API {
return ctx.App.Metadata[metadataAPI].(ApiConnector)()
}

// reqContext returns context for cli execution. Calling it for the first time
// installs SIGTERM handler that will close returned context.
// Not safe for concurrent execution.
func reqContext(cctx *cli.Context) context.Context {
if uctx, ok := cctx.App.Metadata[metadataContext]; ok {
// unchecked cast as if something else is in there
// it is crash worthy either way
return uctx.(context.Context)
}
ctx, done := context.WithCancel(context.Background())
sigChan := make(chan os.Signal, 2)
go func() {
<-sigChan
done()
}()
signal.Notify(sigChan, syscall.SIGTERM, syscall.SIGINT)

return ctx
}

var Commands = []*cli.Command{
netCmd,
versionCmd,
}
164 changes: 164 additions & 0 deletions cli/net.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
package cli

import (
"context"
"fmt"
"sync"
"time"

"github.com/libp2p/go-libp2p-core/peer"
ma "github.com/multiformats/go-multiaddr"
madns "github.com/multiformats/go-multiaddr-dns"
"gopkg.in/urfave/cli.v2"
)

var netCmd = &cli.Command{
Name: "net",
Usage: "Manage P2P Network",
Subcommands: []*cli.Command{
netPeers,
netConnect,
netListen,
},
}

var netPeers = &cli.Command{
Name: "peers",
Usage: "Print peers",
Action: func(cctx *cli.Context) error {
api := getApi(cctx)
ctx := reqContext(cctx)
peers, err := api.NetPeers(ctx)
if err != nil {
return err
}

for _, peer := range peers {
fmt.Println(peer)
}

return nil
},
}

var netListen = &cli.Command{
Name: "listen",
Usage: "List listen addresses",
Action: func(cctx *cli.Context) error {
api := getApi(cctx)
ctx := reqContext(cctx)

addrs, err := api.NetAddrsListen(ctx)
if err != nil {
return err
}

for _, peer := range addrs {
fmt.Println(peer)
}
return nil
},
}

var netConnect = &cli.Command{
Name: "connect",
Usage: "Connect to a peer",
Action: func(cctx *cli.Context) error {
api := getApi(cctx)
ctx := reqContext(cctx)

pis, err := parseAddresses(ctx, cctx.Args().Slice())
if err != nil {
return err
}

for _, pi := range pis {
fmt.Printf("connect %s: ", pi.ID.Pretty())
err := api.NetConnect(ctx, pi)
if err != nil {
fmt.Println("failure")
return err
}
fmt.Println("success")
}

return nil
},
}

// parseAddresses is a function that takes in a slice of string peer addresses
// (multiaddr + peerid) and returns a slice of properly constructed peers
func parseAddresses(ctx context.Context, addrs []string) ([]peer.AddrInfo, error) {
// resolve addresses
maddrs, err := resolveAddresses(ctx, addrs)
if err != nil {
return nil, err
}

return peer.AddrInfosFromP2pAddrs(maddrs...)
}

const (
dnsResolveTimeout = 10 * time.Second
)

// resolveAddresses resolves addresses parallelly
func resolveAddresses(ctx context.Context, addrs []string) ([]ma.Multiaddr, error) {
ctx, cancel := context.WithTimeout(ctx, dnsResolveTimeout)
defer cancel()

var maddrs []ma.Multiaddr
var wg sync.WaitGroup
resolveErrC := make(chan error, len(addrs))

maddrC := make(chan ma.Multiaddr)

for _, addr := range addrs {
maddr, err := ma.NewMultiaddr(addr)
if err != nil {
return nil, err
}

// check whether address ends in `ipfs/Qm...`
if _, last := ma.SplitLast(maddr); last.Protocol().Code == ma.P_IPFS {
maddrs = append(maddrs, maddr)
continue
}
wg.Add(1)
go func(maddr ma.Multiaddr) {
defer wg.Done()
raddrs, err := madns.Resolve(ctx, maddr)
if err != nil {
resolveErrC <- err
return
}
// filter out addresses that still doesn't end in `ipfs/Qm...`
found := 0
for _, raddr := range raddrs {
if _, last := ma.SplitLast(raddr); last != nil && last.Protocol().Code == ma.P_IPFS {
maddrC <- raddr
found++
}
}
if found == 0 {
resolveErrC <- fmt.Errorf("found no ipfs peers at %s", maddr)
}
}(maddr)
}
go func() {
wg.Wait()
close(maddrC)
}()

for maddr := range maddrC {
maddrs = append(maddrs, maddr)
}

select {
case err := <-resolveErrC:
return nil, err
default:
}

return maddrs, nil
}
8 changes: 8 additions & 0 deletions cmd/lotus/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (

"gopkg.in/urfave/cli.v2"

"github.com/filecoin-project/go-lotus/api"
"github.com/filecoin-project/go-lotus/api/client"
"github.com/filecoin-project/go-lotus/build"
lcli "github.com/filecoin-project/go-lotus/cli"
"github.com/filecoin-project/go-lotus/daemon"
Expand All @@ -20,6 +22,12 @@ func main() {
Name: "lotus",
Usage: "Filecoin decentralized storage network client",
Version: build.Version,
Metadata: map[string]interface{}{
"api": lcli.ApiConnector(func() api.API {
// TODO: get this from repo
return client.NewRPC("http://127.0.0.1:1234/rpc/v0")
}),
},

Commands: append(local, lcli.Commands...),
}
Expand Down
10 changes: 9 additions & 1 deletion daemon/cmd.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// +build !nodaemon

package daemon

import (
Expand All @@ -13,6 +15,12 @@ import (
var Cmd = &cli.Command{
Name: "daemon",
Usage: "Start a lotus daemon process",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "api",
Value: ":1234",
},
},
Action: func(cctx *cli.Context) error {
ctx := context.Background()

Expand All @@ -26,6 +34,6 @@ var Cmd = &cli.Command{
return err
}

return serveRPC(api)
return serveRPC(api, cctx.String("api"))
},
}
24 changes: 24 additions & 0 deletions daemon/cmd_nodaemon.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// +build nodaemon

package daemon

import (
"errors"

"gopkg.in/urfave/cli.v2"
)

// Cmd is the `go-lotus daemon` command
var Cmd = &cli.Command{
Name: "daemon",
Usage: "Start a lotus daemon process",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "api",
Value: ":1234",
},
},
Action: func(cctx *cli.Context) error {
return errors.New("daemon support not included in this binary")
},
}
4 changes: 2 additions & 2 deletions daemon/rpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import (
"github.com/filecoin-project/go-lotus/lib/jsonrpc"
)

func serveRPC(api api.API) error {
func serveRPC(api api.API, addr string) error {
rpcServer := jsonrpc.NewServer()
rpcServer.Register("Filecoin", api)
http.Handle("/rpc/v0", rpcServer)
return http.ListenAndServe(":1234", http.DefaultServeMux)
return http.ListenAndServe(addr, http.DefaultServeMux)
}
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ require (
github.com/ipfs/go-ipfs-routing v0.1.0
github.com/ipfs/go-ipld-cbor v0.0.2
github.com/ipfs/go-ipld-format v0.0.2
github.com/ipfs/go-log v0.0.2-0.20190703113630-0c3cfb1eccc4
github.com/ipfs/go-log v0.0.2-0.20190708183747-9c9fd6111bea
github.com/ipfs/go-merkledag v0.0.2
github.com/ipsn/go-secp256k1 v0.0.0-20180726113642-9d62b9f0bc52
github.com/libp2p/go-libp2p v0.2.0
Expand All @@ -38,6 +38,7 @@ require (
github.com/libp2p/go-maddr-filter v0.0.4
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1
github.com/multiformats/go-multiaddr v0.0.4
github.com/multiformats/go-multiaddr-dns v0.0.2
github.com/multiformats/go-multihash v0.0.5
github.com/pkg/errors v0.8.1
github.com/polydawn/refmt v0.0.0-20190408063855-01bf1e26dd14
Expand Down
Loading

0 comments on commit b843dcb

Please sign in to comment.