pubsub cmd: switch to coreapi
License: MIT Signed-off-by: Łukasz Magiera <magik6k@gmail.com>
Łukasz Magiera committed
Sep 11, 2018 at 12:52 UTC
d6aa9527f2b101055df63fe887b8e3c21e8f28f0
4 files changed
+104
-102
core/commands/pubsub.go
+35
-99
@@ -3,23 +3,16 @@ package commands
3
import (
4
"context"
5
"encoding/binary"
6
- "errors"
6
"fmt"
7
"io"
8
"net/http"
9
"sort"
11
- "sync"
12
- "time"
10
14
- core "github.com/ipfs/go-ipfs/core"
11
cmdenv "github.com/ipfs/go-ipfs/core/commands/cmdenv"
12
e "github.com/ipfs/go-ipfs/core/commands/e"
13
+ options "github.com/ipfs/go-ipfs/core/coreapi/interface/options"
14
18
- cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid"
19
- blocks "gx/ipfs/QmRcHuYzAyswytBuMF78rj3LTChYszomRFXNg4685ZN1WM/go-block-format"
20
- pstore "gx/ipfs/QmSJ36wcYQyEViJUWUEhJU81tw1KdakTKqLLHbvYbA9zDv/go-libp2p-peerstore"
15
cmdkit "gx/ipfs/QmSP88ryZkHSRn1fnngAaV2Vcn63WUJzAavnRM9CVdU1Ky/go-ipfs-cmdkit"
22
- floodsub "gx/ipfs/QmUK4h113Hh7bR2gPpsMcbUEbbzc7hspocmPi91Bmi69nH/go-libp2p-floodsub"
16
cmds "gx/ipfs/QmXTmUCBtDUrzDYVzASogLiNph7EBuYqEgPL7QoHNMzUnz/go-ipfs-cmds"
17
)
18
@@ -48,6 +41,13 @@ const (
41
pubsubDiscoverOptionName = "discover"
42
)
43
44
+type pubsubMessage struct {
45
+ From []byte `json:"from,omitempty"`
46
+ Data []byte `json:"data,omitempty"`
47
+ Seqno []byte `json:"seqno,omitempty"`
48
+ TopicIDs []string `json:"topicIDs,omitempty"`
49
+}
50
+
51
var PubsubSubCmd = &cmds.Command{
52
Helptext: cmdkit.HelpText{
53
Tagline: "Subscribe to messages on a given topic.",
@@ -79,40 +79,16 @@ This command outputs data in the following encodings:
79
cmdkit.BoolOption(pubsubDiscoverOptionName, "try to discover other peers subscribed to the same topic"),
80
},
81
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
82
- n, err := cmdenv.GetNode(env)
82
+ api, err := cmdenv.GetApi(env)
83
if err != nil {
84
return err
85
}
86
87
- // Must be online!
88
- if !n.OnlineMode() {
89
- return cmdkit.Errorf(cmdkit.ErrClient, ErrNotOnline.Error())
90
- }
91
-
92
- if n.Floodsub == nil {
93
- return fmt.Errorf("experimental pubsub feature not enabled. Run daemon with --enable-pubsub-experiment to use")
94
- }
95
-
87
topic := req.Arguments[0]
97
- sub, err := n.Floodsub.Subscribe(topic)
98
- if err != nil {
99
- return err
100
- }
101
- defer sub.Cancel()
102
-
88
discover, _ := req.Options[pubsubDiscoverOptionName].(bool)
104
- if discover {
105
- go func() {
106
- blk := blocks.NewBlock([]byte("floodsub:" + topic))
107
- err := n.Blocks.AddBlock(blk)
108
- if err != nil {
109
- log.Error("pubsub discovery: ", err)
110
- return
111
- }
112
-
113
- connectToPubSubPeers(req.Context, n, blk.Cid())
114
- }()
115
- }
89
+
90
+ sub, err := api.PubSub().Subscribe(req.Context, topic, options.PubSub.Discover(discover))
91
+ defer sub.Close()
92
93
if f, ok := res.(http.Flusher); ok {
94
f.Flush()
@@ -126,15 +102,17 @@ This command outputs data in the following encodings:
102
return err
103
}
104
129
- err = res.Emit(msg)
130
- if err != nil {
131
- return err
132
- }
105
+ res.Emit(&pubsubMessage{
106
+ Data: msg.Data(),
107
+ From: []byte(msg.From()),
108
+ Seqno: msg.Seq(),
109
+ TopicIDs: msg.Topics(),
110
+ })
111
}
112
},
113
Encoders: cmds.EncoderMap{
114
cmds.Text: cmds.MakeEncoder(func(req *cmds.Request, w io.Writer, v interface{}) error {
137
- m, ok := v.(*floodsub.Message)
115
+ m, ok := v.(*pubsubMessage)
116
if !ok {
117
return fmt.Errorf("unexpected type: %T", v)
118
}
@@ -143,7 +121,7 @@ This command outputs data in the following encodings:
121
return err
122
}),
123
"ndpayload": cmds.MakeEncoder(func(req *cmds.Request, w io.Writer, v interface{}) error {
146
- m, ok := v.(*floodsub.Message)
124
+ m, ok := v.(*pubsubMessage)
125
if !ok {
126
return fmt.Errorf("unexpected type: %T", v)
127
}
@@ -153,7 +131,7 @@ This command outputs data in the following encodings:
131
return err
132
}),
133
"lenpayload": cmds.MakeEncoder(func(req *cmds.Request, w io.Writer, v interface{}) error {
156
- m, ok := v.(*floodsub.Message)
134
+ m, ok := v.(*pubsubMessage)
135
if !ok {
136
return fmt.Errorf("unexpected type: %T", v)
137
}
@@ -166,31 +144,7 @@ This command outputs data in the following encodings:
144
return err
145
}),
146
},
169
- Type: floodsub.Message{},
170
-}
171
-
172
-func connectToPubSubPeers(ctx context.Context, n *core.IpfsNode, cid cid.Cid) {
173
- ctx, cancel := context.WithCancel(ctx)
174
- defer cancel()
175
-
176
- provs := n.Routing.FindProvidersAsync(ctx, cid, 10)
177
- wg := &sync.WaitGroup{}
178
- for p := range provs {
179
- wg.Add(1)
180
- go func(pi pstore.PeerInfo) {
181
- defer wg.Done()
182
- ctx, cancel := context.WithTimeout(ctx, time.Second*10)
183
- defer cancel()
184
- err := n.PeerHost.Connect(ctx, pi)
185
- if err != nil {
186
- log.Info("pubsub discover: ", err)
187
- return
188
- }
189
- log.Info("connected to pubsub peer:", pi.ID)
190
- }(p)
191
- }
192
-
193
- wg.Wait()
147
+ Type: pubsubMessage{},
148
}
149
150
var PubsubPubCmd = &cmds.Command{
@@ -210,20 +164,11 @@ To use, the daemon must be run with '--enable-pubsub-experiment'.
164
cmdkit.StringArg("data", true, true, "Payload of message to publish.").EnableStdin(),
165
},
166
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
213
- n, err := cmdenv.GetNode(env)
167
+ api, err := cmdenv.GetApi(env)
168
if err != nil {
169
return err
170
}
171
218
- // Must be online!
219
- if !n.OnlineMode() {
220
- return cmdkit.Errorf(cmdkit.ErrClient, ErrNotOnline.Error())
221
- }
222
-
223
- if n.Floodsub == nil {
224
- return errors.New("experimental pubsub feature not enabled. Run daemon with --enable-pubsub-experiment to use.")
225
- }
226
-
172
topic := req.Arguments[0]
173
174
err = req.ParseBodyArgs()
@@ -232,7 +177,7 @@ To use, the daemon must be run with '--enable-pubsub-experiment'.
177
}
178
179
for _, data := range req.Arguments[1:] {
235
- if err := n.Floodsub.Publish(topic, []byte(data)); err != nil {
180
+ if err := api.PubSub().Publish(req.Context, topic, []byte(data)); err != nil {
181
return err
182
}
183
}
@@ -254,21 +199,17 @@ To use, the daemon must be run with '--enable-pubsub-experiment'.
199
`,
200
},
201
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
257
- n, err := cmdenv.GetNode(env)
202
+ api, err := cmdenv.GetApi(env)
203
if err != nil {
204
return err
205
}
206
262
- // Must be online!
263
- if !n.OnlineMode() {
264
- return cmdkit.Errorf(cmdkit.ErrClient, ErrNotOnline.Error())
265
- }
266
-
267
- if n.Floodsub == nil {
268
- return errors.New("experimental pubsub feature not enabled. Run daemon with --enable-pubsub-experiment to use.")
207
+ l, err := api.PubSub().Ls(req.Context)
208
+ if err != nil {
209
+ return err
210
}
211
271
- return cmds.EmitOnce(res, stringList{n.Floodsub.GetTopics()})
212
+ return cmds.EmitOnce(res, stringList{l})
213
},
214
Type: stringList{},
215
Encoders: cmds.EncoderMap{
@@ -308,26 +249,21 @@ To use, the daemon must be run with '--enable-pubsub-experiment'.
249
cmdkit.StringArg("topic", false, false, "topic to list connected peers of"),
250
},
251
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
311
- n, err := cmdenv.GetNode(env)
252
+ api, err := cmdenv.GetApi(env)
253
if err != nil {
254
return err
255
}
256
316
- // Must be online!
317
- if !n.OnlineMode() {
318
- return cmdkit.Errorf(cmdkit.ErrClient, ErrNotOnline.Error())
319
- }
320
-
321
- if n.Floodsub == nil {
322
- return errors.New("experimental pubsub feature not enabled. Run daemon with --enable-pubsub-experiment to use")
323
- }
324
-
257
var topic string
258
if len(req.Arguments) == 1 {
259
topic = req.Arguments[0]
260
}
261
330
- peers := n.Floodsub.ListPeers(topic)
262
+ peers, err := api.PubSub().Peers(req.Context, options.PubSub.Topic(topic))
263
+ if err != nil {
264
+ return err
265
+ }
266
+
267
list := &stringList{make([]string, 0, len(peers))}
268
269
for _, peer := range peers {
core/coreapi/coreapi.go
+4
@@ -16,8 +16,12 @@ package coreapi
16
import (
17
core "github.com/ipfs/go-ipfs/core"
18
coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface"
19
+
20
+ logging "gx/ipfs/QmZChCsSt8DctjceaL56Eibc29CVQq4dGKRXC5JRZ6Ppae/go-log"
21
)
22
23
+var log = logging.Logger("core/coreapi")
24
+
25
type CoreAPI struct {
26
node *core.IpfsNode
27
}
core/coreapi/interface/pubsub.go
+7
-1
@@ -6,7 +6,7 @@ import (
6
7
options "github.com/ipfs/go-ipfs/core/coreapi/interface/options"
8
9
- peer "gx/ipfs/QmQsErDt8Qgw1XrsXf2BpEzDgGWtB1YLsTAARBup5b6B9W/go-libp2p-peer"
9
+ peer "gx/ipfs/QmbNepETomvmXfz1X5pHNFD2QuPqnqi47dTd94QJWSorQ3/go-libp2p-peer"
10
)
11
12
// PubSubSubscription is an active PubSub subscription
@@ -24,6 +24,12 @@ type PubSubMessage interface {
24
25
// Data returns the message body
26
Data() []byte
27
+
28
+ // Seq returns message identifier
29
+ Seq() []byte
30
+
31
+ // Topics returns list of topics this message was set to
32
+ Topics() []string
33
}
34
35
// PubSubAPI specifies the interface to PubSub
core/coreapi/pubsub.go
+58
-2
@@ -3,12 +3,18 @@ package coreapi
3
import (
4
"context"
5
"errors"
6
+ "strings"
7
+ "sync"
8
+ "time"
9
10
+ core "github.com/ipfs/go-ipfs/core"
11
coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface"
12
caopts "github.com/ipfs/go-ipfs/core/coreapi/interface/options"
13
10
- peer "gx/ipfs/QmQsErDt8Qgw1XrsXf2BpEzDgGWtB1YLsTAARBup5b6B9W/go-libp2p-peer"
11
- floodsub "gx/ipfs/QmY1L5krVk8dv8d74uESmJTXGpoigVYqBVxXXz1aS8aFSb/go-libp2p-floodsub"
14
+ cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid"
15
+ pstore "gx/ipfs/QmSJ36wcYQyEViJUWUEhJU81tw1KdakTKqLLHbvYbA9zDv/go-libp2p-peerstore"
16
+ floodsub "gx/ipfs/QmUK4h113Hh7bR2gPpsMcbUEbbzc7hspocmPi91Bmi69nH/go-libp2p-floodsub"
17
+ peer "gx/ipfs/QmbNepETomvmXfz1X5pHNFD2QuPqnqi47dTd94QJWSorQ3/go-libp2p-peer"
18
)
19
20
type PubSubAPI CoreAPI
@@ -58,6 +64,8 @@ func (api *PubSubAPI) Publish(ctx context.Context, topic string, data []byte) er
64
}
65
66
func (api *PubSubAPI) Subscribe(ctx context.Context, topic string, opts ...caopts.PubSubSubscribeOption) (coreiface.PubSubSubscription, error) {
67
+ options, err := caopts.PubSubSubscribeOptions(opts...)
68
+
69
if err := api.checkNode(); err != nil {
70
return nil, err
71
}
@@ -67,9 +75,45 @@ func (api *PubSubAPI) Subscribe(ctx context.Context, topic string, opts ...caopt
75
return nil, err
76
}
77
78
+ if options.Discover {
79
+ go func() {
80
+ blk, err := api.core().Block().Put(ctx, strings.NewReader("floodsub:"+topic))
81
+ if err != nil {
82
+ log.Error("pubsub discovery: ", err)
83
+ return
84
+ }
85
+
86
+ connectToPubSubPeers(ctx, api.node, blk.Path().Cid())
87
+ }()
88
+ }
89
+
90
return &pubSubSubscription{sub}, nil
91
}
92
93
+func connectToPubSubPeers(ctx context.Context, n *core.IpfsNode, cid cid.Cid) {
94
+ ctx, cancel := context.WithCancel(ctx)
95
+ defer cancel()
96
+
97
+ provs := n.Routing.FindProvidersAsync(ctx, cid, 10)
98
+ wg := &sync.WaitGroup{}
99
+ for p := range provs {
100
+ wg.Add(1)
101
+ go func(pi pstore.PeerInfo) {
102
+ defer wg.Done()
103
+ ctx, cancel := context.WithTimeout(ctx, time.Second*10)
104
+ defer cancel()
105
+ err := n.PeerHost.Connect(ctx, pi)
106
+ if err != nil {
107
+ log.Info("pubsub discover: ", err)
108
+ return
109
+ }
110
+ log.Info("connected to pubsub peer:", pi.ID)
111
+ }(p)
112
+ }
113
+
114
+ wg.Wait()
115
+}
116
+
117
func (api *PubSubAPI) checkNode() error {
118
if !api.node.OnlineMode() {
119
return coreiface.ErrOffline
@@ -103,3 +147,15 @@ func (msg *pubSubMessage) From() peer.ID {
147
func (msg *pubSubMessage) Data() []byte {
148
return msg.msg.Data
149
}
150
+
151
+func (msg *pubSubMessage) Seq() []byte {
152
+ return msg.msg.Seqno
153
+}
154
+
155
+func (msg *pubSubMessage) Topics() []string {
156
+ return msg.msg.TopicIDs
157
+}
158
+
159
+func (api *PubSubAPI) core() coreiface.CoreAPI {
160
+ return (*CoreAPI)(api)
161
+}