github.com/keltia/go-ipfs@v0.3.8-0.20150909044612-210793031c63/core/commands/bootstrap.go (about) 1 package commands 2 3 import ( 4 "bytes" 5 "errors" 6 "io" 7 "sort" 8 9 cmds "github.com/ipfs/go-ipfs/commands" 10 repo "github.com/ipfs/go-ipfs/repo" 11 config "github.com/ipfs/go-ipfs/repo/config" 12 "github.com/ipfs/go-ipfs/repo/fsrepo" 13 u "github.com/ipfs/go-ipfs/util" 14 ) 15 16 type BootstrapOutput struct { 17 Peers []string 18 } 19 20 var peerOptionDesc = "A peer to add to the bootstrap list (in the format '<multiaddr>/<peerID>')" 21 22 var BootstrapCmd = &cmds.Command{ 23 Helptext: cmds.HelpText{ 24 Tagline: "Show or edit the list of bootstrap peers", 25 Synopsis: ` 26 ipfs bootstrap list - Show peers in the bootstrap list 27 ipfs bootstrap add <peer>... - Add peers to the bootstrap list 28 ipfs bootstrap rm <peer>... - Removes peers from the bootstrap list 29 `, 30 ShortDescription: ` 31 Running 'ipfs bootstrap' with no arguments will run 'ipfs bootstrap list'. 32 ` + bootstrapSecurityWarning, 33 }, 34 35 Run: bootstrapListCmd.Run, 36 Marshalers: bootstrapListCmd.Marshalers, 37 Type: bootstrapListCmd.Type, 38 39 Subcommands: map[string]*cmds.Command{ 40 "list": bootstrapListCmd, 41 "add": bootstrapAddCmd, 42 "rm": bootstrapRemoveCmd, 43 }, 44 } 45 46 var bootstrapAddCmd = &cmds.Command{ 47 Helptext: cmds.HelpText{ 48 Tagline: "Add peers to the bootstrap list", 49 ShortDescription: `Outputs a list of peers that were added (that weren't already 50 in the bootstrap list). 51 ` + bootstrapSecurityWarning, 52 }, 53 54 Arguments: []cmds.Argument{ 55 cmds.StringArg("peer", false, true, peerOptionDesc).EnableStdin(), 56 }, 57 58 Options: []cmds.Option{ 59 cmds.BoolOption("default", "add default bootstrap nodes"), 60 }, 61 62 Run: func(req cmds.Request, res cmds.Response) { 63 inputPeers, err := config.ParseBootstrapPeers(req.Arguments()) 64 if err != nil { 65 res.SetError(err, cmds.ErrNormal) 66 return 67 } 68 69 r, err := fsrepo.Open(req.InvocContext().ConfigRoot) 70 if err != nil { 71 res.SetError(err, cmds.ErrNormal) 72 return 73 } 74 defer r.Close() 75 cfg, err := r.Config() 76 if err != nil { 77 res.SetError(err, cmds.ErrNormal) 78 return 79 } 80 81 deflt, _, err := req.Option("default").Bool() 82 if err != nil { 83 res.SetError(err, cmds.ErrNormal) 84 return 85 } 86 87 if deflt { 88 // parse separately for meaningful, correct error. 89 defltPeers, err := config.DefaultBootstrapPeers() 90 if err != nil { 91 res.SetError(err, cmds.ErrNormal) 92 return 93 } 94 95 inputPeers = append(inputPeers, defltPeers...) 96 } 97 98 if len(inputPeers) == 0 { 99 res.SetError(errors.New("no bootstrap peers to add"), cmds.ErrClient) 100 return 101 } 102 103 added, err := bootstrapAdd(r, cfg, inputPeers) 104 if err != nil { 105 res.SetError(err, cmds.ErrNormal) 106 return 107 } 108 109 res.SetOutput(&BootstrapOutput{config.BootstrapPeerStrings(added)}) 110 }, 111 Type: BootstrapOutput{}, 112 Marshalers: cmds.MarshalerMap{ 113 cmds.Text: func(res cmds.Response) (io.Reader, error) { 114 v, ok := res.Output().(*BootstrapOutput) 115 if !ok { 116 return nil, u.ErrCast() 117 } 118 119 buf := new(bytes.Buffer) 120 if err := bootstrapWritePeers(buf, "added ", v.Peers); err != nil { 121 return nil, err 122 } 123 124 return buf, nil 125 }, 126 }, 127 } 128 129 var bootstrapRemoveCmd = &cmds.Command{ 130 Helptext: cmds.HelpText{ 131 Tagline: "Removes peers from the bootstrap list", 132 ShortDescription: `Outputs the list of peers that were removed. 133 ` + bootstrapSecurityWarning, 134 }, 135 136 Arguments: []cmds.Argument{ 137 cmds.StringArg("peer", false, true, peerOptionDesc).EnableStdin(), 138 }, 139 Options: []cmds.Option{ 140 cmds.BoolOption("all", "Remove all bootstrap peers."), 141 }, 142 Run: func(req cmds.Request, res cmds.Response) { 143 input, err := config.ParseBootstrapPeers(req.Arguments()) 144 if err != nil { 145 res.SetError(err, cmds.ErrNormal) 146 return 147 } 148 149 r, err := fsrepo.Open(req.InvocContext().ConfigRoot) 150 if err != nil { 151 res.SetError(err, cmds.ErrNormal) 152 return 153 } 154 defer r.Close() 155 cfg, err := r.Config() 156 if err != nil { 157 res.SetError(err, cmds.ErrNormal) 158 return 159 } 160 161 all, _, err := req.Option("all").Bool() 162 if err != nil { 163 res.SetError(err, cmds.ErrNormal) 164 return 165 } 166 167 var removed []config.BootstrapPeer 168 if all { 169 removed, err = bootstrapRemoveAll(r, cfg) 170 } else { 171 removed, err = bootstrapRemove(r, cfg, input) 172 } 173 if err != nil { 174 res.SetError(err, cmds.ErrNormal) 175 return 176 } 177 178 res.SetOutput(&BootstrapOutput{config.BootstrapPeerStrings(removed)}) 179 }, 180 Type: BootstrapOutput{}, 181 Marshalers: cmds.MarshalerMap{ 182 cmds.Text: func(res cmds.Response) (io.Reader, error) { 183 v, ok := res.Output().(*BootstrapOutput) 184 if !ok { 185 return nil, u.ErrCast() 186 } 187 188 buf := new(bytes.Buffer) 189 err := bootstrapWritePeers(buf, "removed ", v.Peers) 190 return buf, err 191 }, 192 }, 193 } 194 195 var bootstrapListCmd = &cmds.Command{ 196 Helptext: cmds.HelpText{ 197 Tagline: "Show peers in the bootstrap list", 198 ShortDescription: "Peers are output in the format '<multiaddr>/<peerID>'.", 199 }, 200 201 Run: func(req cmds.Request, res cmds.Response) { 202 r, err := fsrepo.Open(req.InvocContext().ConfigRoot) 203 if err != nil { 204 res.SetError(err, cmds.ErrNormal) 205 return 206 } 207 defer r.Close() 208 cfg, err := r.Config() 209 if err != nil { 210 res.SetError(err, cmds.ErrNormal) 211 return 212 } 213 214 peers, err := cfg.BootstrapPeers() 215 if err != nil { 216 res.SetError(err, cmds.ErrNormal) 217 return 218 } 219 res.SetOutput(&BootstrapOutput{config.BootstrapPeerStrings(peers)}) 220 return 221 }, 222 Type: BootstrapOutput{}, 223 Marshalers: cmds.MarshalerMap{ 224 cmds.Text: bootstrapMarshaler, 225 }, 226 } 227 228 func bootstrapMarshaler(res cmds.Response) (io.Reader, error) { 229 v, ok := res.Output().(*BootstrapOutput) 230 if !ok { 231 return nil, u.ErrCast() 232 } 233 234 buf := new(bytes.Buffer) 235 err := bootstrapWritePeers(buf, "", v.Peers) 236 return buf, err 237 } 238 239 func bootstrapWritePeers(w io.Writer, prefix string, peers []string) error { 240 241 sort.Stable(sort.StringSlice(peers)) 242 for _, peer := range peers { 243 _, err := w.Write([]byte(peer + "\n")) 244 if err != nil { 245 return err 246 } 247 } 248 return nil 249 } 250 251 func bootstrapAdd(r repo.Repo, cfg *config.Config, peers []config.BootstrapPeer) ([]config.BootstrapPeer, error) { 252 addedMap := map[string]struct{}{} 253 addedList := make([]config.BootstrapPeer, 0, len(peers)) 254 255 // re-add cfg bootstrap peers to rm dupes 256 bpeers := cfg.Bootstrap 257 cfg.Bootstrap = nil 258 259 // add new peers 260 for _, peer := range peers { 261 s := peer.String() 262 if _, found := addedMap[s]; found { 263 continue 264 } 265 266 cfg.Bootstrap = append(cfg.Bootstrap, s) 267 addedList = append(addedList, peer) 268 addedMap[s] = struct{}{} 269 } 270 271 // add back original peers. in this order so that we output them. 272 for _, s := range bpeers { 273 if _, found := addedMap[s]; found { 274 continue 275 } 276 277 cfg.Bootstrap = append(cfg.Bootstrap, s) 278 addedMap[s] = struct{}{} 279 } 280 281 if err := r.SetConfig(cfg); err != nil { 282 return nil, err 283 } 284 285 return addedList, nil 286 } 287 288 func bootstrapRemove(r repo.Repo, cfg *config.Config, toRemove []config.BootstrapPeer) ([]config.BootstrapPeer, error) { 289 removed := make([]config.BootstrapPeer, 0, len(toRemove)) 290 keep := make([]config.BootstrapPeer, 0, len(cfg.Bootstrap)) 291 292 peers, err := cfg.BootstrapPeers() 293 if err != nil { 294 return nil, err 295 } 296 297 for _, peer := range peers { 298 found := false 299 for _, peer2 := range toRemove { 300 if peer.Equal(peer2) { 301 found = true 302 removed = append(removed, peer) 303 break 304 } 305 } 306 307 if !found { 308 keep = append(keep, peer) 309 } 310 } 311 cfg.SetBootstrapPeers(keep) 312 313 if err := r.SetConfig(cfg); err != nil { 314 return nil, err 315 } 316 317 return removed, nil 318 } 319 320 func bootstrapRemoveAll(r repo.Repo, cfg *config.Config) ([]config.BootstrapPeer, error) { 321 removed, err := cfg.BootstrapPeers() 322 if err != nil { 323 return nil, err 324 } 325 326 cfg.Bootstrap = nil 327 if err := r.SetConfig(cfg); err != nil { 328 return nil, err 329 } 330 331 return removed, nil 332 } 333 334 const bootstrapSecurityWarning = ` 335 SECURITY WARNING: 336 337 The bootstrap command manipulates the "bootstrap list", which contains 338 the addresses of bootstrap nodes. These are the *trusted peers* from 339 which to learn about other peers in the network. Only edit this list 340 if you understand the risks of adding or removing nodes from this list. 341 342 `