feature: 'ipfs swarm peering' command (#8147)
* feat: added swarm peering command supporting add, ls and rm Co-authored-by: Steven Allen <steven@stebalien.com>
Takashi Matsuda committed
Sep 15, 2021 at 23:46 UTC
a651045c502435b5dfea0d928dc243a7c02b6b04
5 files changed
+199
core/commands/commands_test.go
+4
@@ -237,6 +237,10 @@ func TestCommands(t *testing.T) {
237
"/swarm/filters/add",
238
"/swarm/filters/rm",
239
"/swarm/peers",
240
+ "/swarm/peering",
241
+ "/swarm/peering/add",
242
+ "/swarm/peering/ls",
243
+ "/swarm/peering/rm",
244
"/tar",
245
"/tar/add",
246
"/tar/cat",
core/commands/swarm.go
+144
@@ -51,6 +51,7 @@ ipfs peers in the internet.
51
"disconnect": swarmDisconnectCmd,
52
"filters": swarmFiltersCmd,
53
"peers": swarmPeersCmd,
54
+ "peering": swarmPeeringCmd,
55
},
56
}
57
@@ -61,6 +62,149 @@ const (
62
swarmDirectionOptionName = "direction"
63
)
64
65
+type peeringResult struct {
66
+ ID peer.ID
67
+ Status string
68
+}
69
+
70
+var swarmPeeringCmd = &cmds.Command{
71
+ Helptext: cmds.HelpText{
72
+ Tagline: "Modify the peering subsystem.",
73
+ ShortDescription: `
74
+'ipfs swarm peering' manages the peering subsystem.
75
+Peers in the peering subsystem is maintained to be connected, reconnected
76
+on disconnect with a back-off.
77
+The changes are not saved to the config.
78
+`,
79
+ },
80
+ Subcommands: map[string]*cmds.Command{
81
+ "add": swarmPeeringAddCmd,
82
+ "ls": swarmPeeringLsCmd,
83
+ "rm": swarmPeeringRmCmd,
84
+ },
85
+}
86
+
87
+var swarmPeeringAddCmd = &cmds.Command{
88
+ Helptext: cmds.HelpText{
89
+ Tagline: "Add peers into the peering subsystem.",
90
+ ShortDescription: `
91
+'ipfs swarm peering add' will add the new address to the peering subsystem as one that should always be connected to.
92
+`,
93
+ },
94
+ Arguments: []cmds.Argument{
95
+ cmds.StringArg("address", true, true, "address of peer to add into the peering subsystem"),
96
+ },
97
+ Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
98
+ addrs := make([]ma.Multiaddr, len(req.Arguments))
99
+
100
+ for i, arg := range req.Arguments {
101
+ addr, err := ma.NewMultiaddr(arg)
102
+ if err != nil {
103
+ return err
104
+ }
105
+
106
+ addrs[i] = addr
107
+ }
108
+
109
+ addInfos, err := peer.AddrInfosFromP2pAddrs(addrs...)
110
+ if err != nil {
111
+ return err
112
+ }
113
+
114
+ node, err := cmdenv.GetNode(env)
115
+ if err != nil {
116
+ return err
117
+ }
118
+
119
+ for _, addrinfo := range addInfos {
120
+ node.Peering.AddPeer(addrinfo)
121
+ err = res.Emit(peeringResult{addrinfo.ID, "success"})
122
+ if err != nil {
123
+ return err
124
+ }
125
+ }
126
+ return nil
127
+ },
128
+ Encoders: cmds.EncoderMap{
129
+ cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, pr *peeringResult) error {
130
+ fmt.Fprintf(w, "add %s %s\n", pr.ID.String(), pr.Status)
131
+ return nil
132
+ }),
133
+ },
134
+ Type: peeringResult{},
135
+}
136
+
137
+var swarmPeeringLsCmd = &cmds.Command{
138
+ Helptext: cmds.HelpText{
139
+ Tagline: "List peers registered in the peering subsystem.",
140
+ ShortDescription: `
141
+'ipfs swarm peering ls' lists the peers that are registered in the peering subsystem and to which the daemon is always connected.
142
+`,
143
+ },
144
+ Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
145
+ node, err := cmdenv.GetNode(env)
146
+ if err != nil {
147
+ return err
148
+ }
149
+ peers := node.Peering.ListPeers()
150
+ return cmds.EmitOnce(res, addrInfos{Peers: peers})
151
+ },
152
+ Type: addrInfos{},
153
+ Encoders: cmds.EncoderMap{
154
+ cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, ai *addrInfos) error {
155
+ for _, info := range ai.Peers {
156
+ fmt.Fprintf(w, "%s\n", info.ID)
157
+ for _, addr := range info.Addrs {
158
+ fmt.Fprintf(w, "\t%s\n", addr)
159
+ }
160
+ }
161
+ return nil
162
+ }),
163
+ },
164
+}
165
+
166
+type addrInfos struct {
167
+ Peers []peer.AddrInfo
168
+}
169
+
170
+var swarmPeeringRmCmd = &cmds.Command{
171
+ Helptext: cmds.HelpText{
172
+ Tagline: "Remove a peer from the peering subsystem.",
173
+ ShortDescription: `
174
+'ipfs swarm peering rm' will remove the given ID from the peering subsystem and remove it from the always-on connection.
175
+`,
176
+ },
177
+ Arguments: []cmds.Argument{
178
+ cmds.StringArg("ID", true, true, "ID of peer to remove from the peering subsystem"),
179
+ },
180
+ Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
181
+ node, err := cmdenv.GetNode(env)
182
+ if err != nil {
183
+ return err
184
+ }
185
+
186
+ for _, arg := range req.Arguments {
187
+ id, err := peer.Decode(arg)
188
+ if err != nil {
189
+ return err
190
+ }
191
+
192
+ node.Peering.RemovePeer(id)
193
+ if err = res.Emit(peeringResult{id, "success"}); err != nil {
194
+ return err
195
+ }
196
+ }
197
+ return nil
198
+ },
199
+ Type: peeringResult{},
200
+ Encoders: cmds.EncoderMap{
201
+ cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, pr *peeringResult) error {
202
+ fmt.Fprintf(w, "add %s %s\n", pr.ID.String(), pr.Status)
203
+ return nil
204
+ }),
205
+ },
206
+}
207
+
208
var swarmPeersCmd = &cmds.Command{
209
Helptext: cmds.HelpText{
210
Tagline: "List peers with open connections.",
peering/peering.go
+11
@@ -242,6 +242,17 @@ func (ps *PeeringService) AddPeer(info peer.AddrInfo) {
242
}
243
}
244
245
+// ListPeers lists peers in the peering service.
246
+func (ps *PeeringService) ListPeers() []peer.AddrInfo {
247
+ out := make([]peer.AddrInfo, 0, len(ps.peers))
248
+ for id, addrs := range ps.peers {
249
+ ai := peer.AddrInfo{ID: id}
250
+ ai.Addrs = append(ai.Addrs, addrs.addrs...)
251
+ out = append(out, ai)
252
+ }
253
+ return out
254
+}
255
+
256
// RemovePeer removes a peer from the peering service. This function may be
257
// safely called at any time: before the service is started, while running, or
258
// after it stops.
peering/peering_test.go
+6
@@ -39,6 +39,7 @@ func TestPeeringService(t *testing.T) {
39
40
// peer 1 -> 2
41
ps1.AddPeer(peer.AddrInfo{ID: h2.ID(), Addrs: h2.Addrs()})
42
+ require.Contains(t, ps1.ListPeers(), peer.AddrInfo{ID: h2.ID(), Addrs: h2.Addrs()})
43
44
// We haven't started so we shouldn't have any peers.
45
require.Never(t, func() bool {
@@ -109,6 +110,7 @@ func TestPeeringService(t *testing.T) {
110
111
// Unprotect 2 from 1.
112
ps1.RemovePeer(h2.ID())
113
+ require.NotContains(t, ps1.ListPeers(), peer.AddrInfo{ID: h2.ID(), Addrs: h2.Addrs()})
114
115
// Trim connections.
116
h1.ConnManager().TrimOpenConns(ctx)
@@ -127,7 +129,9 @@ func TestPeeringService(t *testing.T) {
129
130
// Until added back
131
ps1.AddPeer(peer.AddrInfo{ID: h2.ID(), Addrs: h2.Addrs()})
132
+ require.Contains(t, ps1.ListPeers(), peer.AddrInfo{ID: h2.ID(), Addrs: h2.Addrs()})
133
ps1.AddPeer(peer.AddrInfo{ID: h3.ID(), Addrs: h3.Addrs()})
134
+ require.Contains(t, ps1.ListPeers(), peer.AddrInfo{ID: h3.ID(), Addrs: h3.Addrs()})
135
t.Logf("wait for h1 to connect to h2 and h3 again")
136
require.Eventually(t, func() bool {
137
return h1.Network().Connectedness(h2.ID()) == network.Connected
@@ -142,7 +146,9 @@ func TestPeeringService(t *testing.T) {
146
147
// Adding and removing should work after stopping.
148
ps1.AddPeer(peer.AddrInfo{ID: h4.ID(), Addrs: h4.Addrs()})
149
+ require.Contains(t, ps1.ListPeers(), peer.AddrInfo{ID: h4.ID(), Addrs: h4.Addrs()})
150
ps1.RemovePeer(h2.ID())
151
+ require.NotContains(t, ps1.ListPeers(), peer.AddrInfo{ID: h2.ID(), Addrs: h2.Addrs()})
152
}
153
154
func TestNextBackoff(t *testing.T) {
test/sharness/t0140-swarm.sh
+34
@@ -97,6 +97,40 @@ test_expect_success "Addresses.NoAnnounce with /ipcidr affects addresses" '
97
98
test_kill_ipfs_daemon
99
100
+test_launch_ipfs_daemon
101
+
102
+test_expect_success "'ipfs swarm peering ls' lists peerings" '
103
+ ipfs swarm peering ls
104
+'
105
+
106
+peeringID='QmYyQSo1c1Ym7orWxLYvCrM2EmxFTANf8wXmmE7DWjhx5N'
107
+peeringID2='QmYyQSo1c1Ym7orWxLYvCrM2EmxFTANf8wXmmE7DWjhx5K'
108
+peeringAddr='/ip4/1.2.3.4/tcp/1234/p2p/QmYyQSo1c1Ym7orWxLYvCrM2EmxFTANf8wXmmE7DWjhx5N'
109
+peeringAddr2='/ip4/1.2.3.4/tcp/1234/p2p/QmYyQSo1c1Ym7orWxLYvCrM2EmxFTANf8wXmmE7DWjhx5K'
110
+test_expect_success "'ipfs swarm peering add' adds a peering" '
111
+ ipfs swarm peering ls > peeringls &&
112
+ ! test_should_contain ${peeringID} peeringls &&
113
+ ! test_should_contain ${peeringID2} peeringls &&
114
+ ipfs swarm peering add ${peeringAddr} ${peeringAddr2}
115
+'
116
+
117
+test_expect_success 'a peering is added' '
118
+ ipfs swarm peering ls > peeringadd &&
119
+ test_should_contain ${peeringID} peeringadd &&
120
+ test_should_contain ${peeringID2} peeringadd
121
+'
122
+
123
+test_expect_success "'swarm peering rm' removes a peering" '
124
+ ipfs swarm peering rm ${peeringID}
125
+'
126
+
127
+test_expect_success 'peering is removed' '
128
+ ipfs swarm peering ls > peeringrm &&
129
+ ! test_should_contain ${peeringID} peeringrm
130
+'
131
+
132
+test_kill_ipfs_daemon
133
+
134
test_expect_success "set up tcp testbed" '
135
iptb testbed create -type localipfs -count 2 -force -init
136
'