core/commands2: Added 'bootstrap' command
Matt Bell committed
Nov 5, 2014 at 22:22 UTC
16c584ce93aea721527eb310c524054cbd1c2ab5
2 files changed
+264
-11
core/commands2/bootstrap.go
new
+252
@@ -0,0 +1,252 @@
1
+package commands
2
+
3
+import (
4
+ "errors"
5
+ "fmt"
6
+ "strings"
7
+
8
+ ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
9
+ mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash"
10
+
11
+ cmds "github.com/jbenet/go-ipfs/commands"
12
+ config "github.com/jbenet/go-ipfs/config"
13
+ //peer "github.com/jbenet/go-ipfs/peer"
14
+ //u "github.com/jbenet/go-ipfs/util"
15
+)
16
+
17
+type BootstrapOutput struct {
18
+ Peers []*config.BootstrapPeer
19
+}
20
+
21
+var bootstrapCmd = &cmds.Command{
22
+ Help: `ipfs bootstrap - show, or manipulate bootstrap node addresses
23
+
24
+Running 'ipfs bootstrap' with no arguments will run 'ipfs bootstrap list'.
25
+
26
+Commands:
27
+
28
+ list Show the boostrap list.
29
+ add <address> Add a node's address to the bootstrap list.
30
+ remove <address> Remove an address from the bootstrap list.
31
+
32
+` + bootstrapSecurityWarning,
33
+ Run: bootstrapListCmd.Run,
34
+ Marshallers: bootstrapListCmd.Marshallers,
35
+ Subcommands: map[string]*cmds.Command{
36
+ "list": bootstrapListCmd,
37
+ "add": bootstrapAddCmd,
38
+ "remove": bootstrapRemoveCmd,
39
+ },
40
+}
41
+
42
+var bootstrapAddCmd = &cmds.Command{
43
+ Arguments: []cmds.Argument{
44
+ cmds.Argument{"peer", cmds.ArgString, true, true},
45
+ },
46
+ Help: `ipfs bootstrap add - add addresses to the bootstrap list
47
+` + bootstrapSecurityWarning,
48
+ Run: func(res cmds.Response, req cmds.Request) {
49
+ input, err := bootstrapInputToPeers(req.Arguments())
50
+ if err != nil {
51
+ res.SetError(err, cmds.ErrNormal)
52
+ return
53
+ }
54
+
55
+ filename, err := config.Filename(req.Context().ConfigRoot)
56
+ if err != nil {
57
+ res.SetError(err, cmds.ErrNormal)
58
+ return
59
+ }
60
+
61
+ added, err := bootstrapAdd(filename, req.Context().Config, input)
62
+ if err != nil {
63
+ res.SetError(err, cmds.ErrNormal)
64
+ return
65
+ }
66
+
67
+ res.SetOutput(&BootstrapOutput{added})
68
+ },
69
+ Type: &BootstrapOutput{},
70
+ Marshallers: map[cmds.EncodingType]cmds.Marshaller{
71
+ cmds.Text: func(res cmds.Response) ([]byte, error) {
72
+ v := res.Output().(*BootstrapOutput)
73
+ s := fmt.Sprintf("Added %v peers to the bootstrap list:\n", len(v.Peers))
74
+ marshalled, err := bootstrapMarshaller(res)
75
+ if err != nil {
76
+ return nil, err
77
+ }
78
+ return append([]byte(s), marshalled...), nil
79
+ },
80
+ },
81
+}
82
+
83
+var bootstrapRemoveCmd = &cmds.Command{
84
+ Arguments: []cmds.Argument{
85
+ cmds.Argument{"peer", cmds.ArgString, true, true},
86
+ },
87
+ Help: `ipfs bootstrap remove - remove addresses from the bootstrap list
88
+` + bootstrapSecurityWarning,
89
+ Run: func(res cmds.Response, req cmds.Request) {
90
+ input, err := bootstrapInputToPeers(req.Arguments())
91
+ if err != nil {
92
+ res.SetError(err, cmds.ErrNormal)
93
+ return
94
+ }
95
+
96
+ filename, err := config.Filename(req.Context().ConfigRoot)
97
+ if err != nil {
98
+ res.SetError(err, cmds.ErrNormal)
99
+ return
100
+ }
101
+
102
+ removed, err := bootstrapRemove(filename, req.Context().Config, input)
103
+ if err != nil {
104
+ res.SetError(err, cmds.ErrNormal)
105
+ return
106
+ }
107
+
108
+ res.SetOutput(&BootstrapOutput{removed})
109
+ },
110
+ Type: &BootstrapOutput{},
111
+ Marshallers: map[cmds.EncodingType]cmds.Marshaller{
112
+ cmds.Text: func(res cmds.Response) ([]byte, error) {
113
+ v := res.Output().(*BootstrapOutput)
114
+ s := fmt.Sprintf("Removed %v peers from the bootstrap list:\n", len(v.Peers))
115
+ marshalled, err := bootstrapMarshaller(res)
116
+ if err != nil {
117
+ return nil, err
118
+ }
119
+ return append([]byte(s), marshalled...), nil
120
+ },
121
+ },
122
+}
123
+
124
+var bootstrapListCmd = &cmds.Command{
125
+ Help: "ipfs bootstrap list - Show addresses in the bootstrap list",
126
+ Run: func(res cmds.Response, req cmds.Request) {
127
+ peers := req.Context().Config.Bootstrap
128
+ res.SetOutput(&BootstrapOutput{peers})
129
+ },
130
+ Type: &BootstrapOutput{},
131
+ Marshallers: map[cmds.EncodingType]cmds.Marshaller{
132
+ cmds.Text: bootstrapMarshaller,
133
+ },
134
+}
135
+
136
+func bootstrapMarshaller(res cmds.Response) ([]byte, error) {
137
+ v := res.Output().(*BootstrapOutput)
138
+
139
+ s := ""
140
+ for _, peer := range v.Peers {
141
+ s += fmt.Sprintf("%s/%s\n", peer.Address, peer.PeerID)
142
+ }
143
+
144
+ return []byte(s), nil
145
+}
146
+
147
+func bootstrapInputToPeers(input []interface{}) ([]*config.BootstrapPeer, error) {
148
+ split := func(addr string) (string, string) {
149
+ idx := strings.LastIndex(addr, "/")
150
+ if idx == -1 {
151
+ return "", addr
152
+ }
153
+ return addr[:idx], addr[idx+1:]
154
+ }
155
+
156
+ peers := []*config.BootstrapPeer{}
157
+ for _, v := range input {
158
+ addr, ok := v.(string)
159
+ if !ok {
160
+ return nil, errors.New("cast error")
161
+ }
162
+
163
+ addrS, peeridS := split(addr)
164
+
165
+ // make sure addrS parses as a multiaddr.
166
+ if len(addrS) > 0 {
167
+ maddr, err := ma.NewMultiaddr(addrS)
168
+ if err != nil {
169
+ return nil, err
170
+ }
171
+
172
+ addrS = maddr.String()
173
+ }
174
+
175
+ // make sure idS parses as a peer.ID
176
+ _, err := mh.FromB58String(peeridS)
177
+ if err != nil {
178
+ return nil, err
179
+ }
180
+
181
+ // construct config entry
182
+ peers = append(peers, &config.BootstrapPeer{
183
+ Address: addrS,
184
+ PeerID: peeridS,
185
+ })
186
+ }
187
+ return peers, nil
188
+}
189
+
190
+func bootstrapAdd(filename string, cfg *config.Config, peers []*config.BootstrapPeer) ([]*config.BootstrapPeer, error) {
191
+ added := make([]*config.BootstrapPeer, 0, len(peers))
192
+
193
+ for _, peer := range peers {
194
+ duplicate := false
195
+ for _, peer2 := range cfg.Bootstrap {
196
+ if peer.Address == peer2.Address {
197
+ duplicate = true
198
+ break
199
+ }
200
+ }
201
+
202
+ if !duplicate {
203
+ cfg.Bootstrap = append(cfg.Bootstrap, peer)
204
+ added = append(added, peer)
205
+ }
206
+ }
207
+
208
+ err := config.WriteConfigFile(filename, cfg)
209
+ if err != nil {
210
+ return nil, err
211
+ }
212
+
213
+ return added, nil
214
+}
215
+
216
+func bootstrapRemove(filename string, cfg *config.Config, peers []*config.BootstrapPeer) ([]*config.BootstrapPeer, error) {
217
+ removed := make([]*config.BootstrapPeer, 0, len(peers))
218
+ keep := make([]*config.BootstrapPeer, 0, len(cfg.Bootstrap))
219
+
220
+ for _, peer := range cfg.Bootstrap {
221
+ found := false
222
+ for _, peer2 := range peers {
223
+ if peer.Address == peer2.Address && peer.PeerID == peer2.PeerID {
224
+ found = true
225
+ removed = append(removed, peer)
226
+ break
227
+ }
228
+ }
229
+
230
+ if !found {
231
+ keep = append(keep, peer)
232
+ }
233
+ }
234
+ cfg.Bootstrap = keep
235
+
236
+ err := config.WriteConfigFile(filename, cfg)
237
+ if err != nil {
238
+ return nil, err
239
+ }
240
+
241
+ return removed, nil
242
+}
243
+
244
+const bootstrapSecurityWarning = `
245
+SECURITY WARNING:
246
+
247
+The bootstrap command manipulates the "bootstrap list", which contains
248
+the addresses of bootstrap nodes. These are the *trusted peers* from
249
+which to learn about other peers in the network. Only edit this list
250
+if you understand the risks of adding or removing nodes from this list.
251
+
252
+`
core/commands2/root.go
+12
-11
@@ -56,17 +56,18 @@ Use "ipfs help <command>" for more information about a command.
56
}
57
58
var rootSubcommands = map[string]*cmds.Command{
59
- "cat": catCmd,
60
- "ls": lsCmd,
61
- "commands": commandsCmd,
62
- "name": nameCmd,
63
- "add": addCmd,
64
- "log": logCmd,
65
- "diag": diagCmd,
66
- "pin": pinCmd,
67
- "unpin": unpinCmd,
68
- "version": versionCmd,
69
- "config": configCmd,
59
+ "cat": catCmd,
60
+ "ls": lsCmd,
61
+ "commands": commandsCmd,
62
+ "name": nameCmd,
63
+ "add": addCmd,
64
+ "log": logCmd,
65
+ "diag": diagCmd,
66
+ "pin": pinCmd,
67
+ "unpin": unpinCmd,
68
+ "version": versionCmd,
69
+ "config": configCmd,
70
+ "bootstrap": bootstrapCmd,
71
72
// test subcommands
73
// TODO: remove these when we don't need them anymore