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  `