@cryptotaxi247 / kubo / commits / a651045c5

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 '