OSDN Git Service

f0b24f878b5fcf86b868fcca25a01203325d61cf
[bytom/vapor.git] / vendor / github.com / ipfs / go-ipfs-api / shell.go
1 // package shell implements a remote API interface for a running ipfs daemon
2 package shell
3
4 import (
5         "bytes"
6         "context"
7         "encoding/json"
8         "errors"
9         "fmt"
10         "io"
11         "io/ioutil"
12         gohttp "net/http"
13         "os"
14         "path"
15         "strings"
16         "time"
17
18         files "github.com/ipfs/go-ipfs-files"
19         homedir "github.com/mitchellh/go-homedir"
20         ma "github.com/multiformats/go-multiaddr"
21         manet "github.com/multiformats/go-multiaddr-net"
22         tar "github.com/whyrusleeping/tar-utils"
23
24         p2pmetrics "github.com/libp2p/go-libp2p-metrics"
25 )
26
27 const (
28         DefaultPathName = ".ipfs"
29         DefaultPathRoot = "~/" + DefaultPathName
30         DefaultApiFile  = "api"
31         EnvDir          = "IPFS_PATH"
32 )
33
34 type Shell struct {
35         url     string
36         httpcli gohttp.Client
37 }
38
39 func NewLocalShell() *Shell {
40         baseDir := os.Getenv(EnvDir)
41         if baseDir == "" {
42                 baseDir = DefaultPathRoot
43         }
44
45         baseDir, err := homedir.Expand(baseDir)
46         if err != nil {
47                 return nil
48         }
49
50         apiFile := path.Join(baseDir, DefaultApiFile)
51
52         if _, err := os.Stat(apiFile); err != nil {
53                 return nil
54         }
55
56         api, err := ioutil.ReadFile(apiFile)
57         if err != nil {
58                 return nil
59         }
60
61         return NewShell(strings.TrimSpace(string(api)))
62 }
63
64 func NewShell(url string) *Shell {
65         c := &gohttp.Client{
66                 Transport: &gohttp.Transport{
67                         Proxy:             gohttp.ProxyFromEnvironment,
68                         DisableKeepAlives: true,
69                 },
70         }
71
72         return NewShellWithClient(url, c)
73 }
74
75 func NewShellWithClient(url string, c *gohttp.Client) *Shell {
76         if a, err := ma.NewMultiaddr(url); err == nil {
77                 _, host, err := manet.DialArgs(a)
78                 if err == nil {
79                         url = host
80                 }
81         }
82         var sh Shell
83         sh.url = url
84         sh.httpcli = *c
85         // We don't support redirects.
86         sh.httpcli.CheckRedirect = func(_ *gohttp.Request, _ []*gohttp.Request) error {
87                 return fmt.Errorf("unexpected redirect")
88         }
89         return &sh
90 }
91
92 func (s *Shell) SetTimeout(d time.Duration) {
93         s.httpcli.Timeout = d
94 }
95
96 func (s *Shell) Request(command string, args ...string) *RequestBuilder {
97         return &RequestBuilder{
98                 command: command,
99                 args:    args,
100                 shell:   s,
101         }
102 }
103
104 type IdOutput struct {
105         ID              string
106         PublicKey       string
107         Addresses       []string
108         AgentVersion    string
109         ProtocolVersion string
110 }
111
112 // ID gets information about a given peer.  Arguments:
113 //
114 // peer: peer.ID of the node to look up.  If no peer is specified,
115 //   return information about the local peer.
116 func (s *Shell) ID(peer ...string) (*IdOutput, error) {
117         if len(peer) > 1 {
118                 return nil, fmt.Errorf("Too many peer arguments")
119         }
120
121         var out IdOutput
122         if err := s.Request("id", peer...).Exec(context.Background(), &out); err != nil {
123                 return nil, err
124         }
125         return &out, nil
126 }
127
128 // Cat the content at the given path. Callers need to drain and close the returned reader after usage.
129 func (s *Shell) Cat(path string) (io.ReadCloser, error) {
130         resp, err := s.Request("cat", path).Send(context.Background())
131         if err != nil {
132                 return nil, err
133         }
134         if resp.Error != nil {
135                 return nil, resp.Error
136         }
137
138         return resp.Output, nil
139 }
140
141 const (
142         TRaw = iota
143         TDirectory
144         TFile
145         TMetadata
146         TSymlink
147 )
148
149 // List entries at the given path
150 func (s *Shell) List(path string) ([]*LsLink, error) {
151         var out struct{ Objects []LsObject }
152         err := s.Request("ls", path).Exec(context.Background(), &out)
153         if err != nil {
154                 return nil, err
155         }
156         if len(out.Objects) != 1 {
157                 return nil, errors.New("bad response from server")
158         }
159         return out.Objects[0].Links, nil
160 }
161
162 type LsLink struct {
163         Hash string
164         Name string
165         Size uint64
166         Type int
167 }
168
169 type LsObject struct {
170         Links []*LsLink
171         LsLink
172 }
173
174 // Pin the given path
175 func (s *Shell) Pin(path string) error {
176         return s.Request("pin/add", path).
177                 Option("recursive", true).
178                 Exec(context.Background(), nil)
179 }
180
181 // Unpin the given path
182 func (s *Shell) Unpin(path string) error {
183         return s.Request("pin/rm", path).
184                 Option("recursive", true).
185                 Exec(context.Background(), nil)
186 }
187
188 const (
189         DirectPin    = "direct"
190         RecursivePin = "recursive"
191         IndirectPin  = "indirect"
192 )
193
194 type PinInfo struct {
195         Type string
196 }
197
198 // Pins returns a map of the pin hashes to their info (currently just the
199 // pin type, one of DirectPin, RecursivePin, or IndirectPin. A map is returned
200 // instead of a slice because it is easier to do existence lookup by map key
201 // than unordered array searching. The map is likely to be more useful to a
202 // client than a flat list.
203 func (s *Shell) Pins() (map[string]PinInfo, error) {
204         var raw struct{ Keys map[string]PinInfo }
205         return raw.Keys, s.Request("pin/ls").Exec(context.Background(), &raw)
206 }
207
208 type PeerInfo struct {
209         Addrs []string
210         ID    string
211 }
212
213 func (s *Shell) FindPeer(peer string) (*PeerInfo, error) {
214         var peers struct{ Responses []PeerInfo }
215         err := s.Request("dht/findpeer", peer).Exec(context.Background(), &peers)
216         if err != nil {
217                 return nil, err
218         }
219         if len(peers.Responses) == 0 {
220                 return nil, errors.New("peer not found")
221         }
222         return &peers.Responses[0], nil
223 }
224
225 func (s *Shell) Refs(hash string, recursive bool) (<-chan string, error) {
226         resp, err := s.Request("refs", hash).
227                 Option("recursive", recursive).
228                 Send(context.Background())
229         if err != nil {
230                 return nil, err
231         }
232
233         if resp.Error != nil {
234                 resp.Close()
235                 return nil, resp.Error
236         }
237
238         out := make(chan string)
239         go func() {
240                 defer resp.Close()
241                 var ref struct {
242                         Ref string
243                 }
244                 defer close(out)
245                 dec := json.NewDecoder(resp.Output)
246                 for {
247                         err := dec.Decode(&ref)
248                         if err != nil {
249                                 return
250                         }
251                         if len(ref.Ref) > 0 {
252                                 out <- ref.Ref
253                         }
254                 }
255         }()
256
257         return out, nil
258 }
259
260 func (s *Shell) Patch(root, action string, args ...string) (string, error) {
261         var out object
262         return out.Hash, s.Request("object/patch/"+action, root).
263                 Arguments(args...).
264                 Exec(context.Background(), &out)
265 }
266
267 func (s *Shell) PatchData(root string, set bool, data interface{}) (string, error) {
268         var read io.Reader
269         switch d := data.(type) {
270         case io.Reader:
271                 read = d
272         case []byte:
273                 read = bytes.NewReader(d)
274         case string:
275                 read = strings.NewReader(d)
276         default:
277                 return "", fmt.Errorf("unrecognized type: %#v", data)
278         }
279
280         cmd := "append-data"
281         if set {
282                 cmd = "set-data"
283         }
284
285         fr := files.NewReaderFile(read)
286         slf := files.NewSliceDirectory([]files.DirEntry{files.FileEntry("", fr)})
287         fileReader := files.NewMultiFileReader(slf, true)
288
289         var out object
290         return out.Hash, s.Request("object/patch/"+cmd, root).
291                 Body(fileReader).
292                 Exec(context.Background(), &out)
293 }
294
295 func (s *Shell) PatchLink(root, path, childhash string, create bool) (string, error) {
296         var out object
297         return out.Hash, s.Request("object/patch/add-link", root, path, childhash).
298                 Option("create", create).
299                 Exec(context.Background(), &out)
300 }
301
302 func (s *Shell) Get(hash, outdir string) error {
303         resp, err := s.Request("get", hash).Option("create", true).Send(context.Background())
304         if err != nil {
305                 return err
306         }
307         defer resp.Close()
308
309         if resp.Error != nil {
310                 return resp.Error
311         }
312
313         extractor := &tar.Extractor{Path: outdir}
314         return extractor.Extract(resp.Output)
315 }
316
317 func (s *Shell) NewObject(template string) (string, error) {
318         var out object
319         req := s.Request("object/new")
320         if template != "" {
321                 req.Arguments(template)
322         }
323         return out.Hash, req.Exec(context.Background(), &out)
324 }
325
326 func (s *Shell) ResolvePath(path string) (string, error) {
327         var out struct {
328                 Path string
329         }
330         err := s.Request("resolve", path).Exec(context.Background(), &out)
331         if err != nil {
332                 return "", err
333         }
334
335         return strings.TrimPrefix(out.Path, "/ipfs/"), nil
336 }
337
338 // returns ipfs version and commit sha
339 func (s *Shell) Version() (string, string, error) {
340         ver := struct {
341                 Version string
342                 Commit  string
343         }{}
344
345         if err := s.Request("version").Exec(context.Background(), &ver); err != nil {
346                 return "", "", err
347         }
348         return ver.Version, ver.Commit, nil
349 }
350
351 func (s *Shell) IsUp() bool {
352         _, _, err := s.Version()
353         return err == nil
354 }
355
356 func (s *Shell) BlockStat(path string) (string, int, error) {
357         var inf struct {
358                 Key  string
359                 Size int
360         }
361
362         if err := s.Request("block/stat", path).Exec(context.Background(), &inf); err != nil {
363                 return "", 0, err
364         }
365         return inf.Key, inf.Size, nil
366 }
367
368 func (s *Shell) BlockGet(path string) ([]byte, error) {
369         resp, err := s.Request("block/get", path).Send(context.Background())
370         if err != nil {
371                 return nil, err
372         }
373         defer resp.Close()
374
375         if resp.Error != nil {
376                 return nil, resp.Error
377         }
378
379         return ioutil.ReadAll(resp.Output)
380 }
381
382 func (s *Shell) BlockPut(block []byte, format, mhtype string, mhlen int) (string, error) {
383         var out struct {
384                 Key string
385         }
386
387         fr := files.NewBytesFile(block)
388         slf := files.NewSliceDirectory([]files.DirEntry{files.FileEntry("", fr)})
389         fileReader := files.NewMultiFileReader(slf, true)
390
391         return out.Key, s.Request("block/put").
392                 Option("mhtype", mhtype).
393                 Option("format", format).
394                 Option("mhlen", mhlen).
395                 Body(fileReader).
396                 Exec(context.Background(), &out)
397 }
398
399 type IpfsObject struct {
400         Links []ObjectLink
401         Data  string
402 }
403
404 type ObjectLink struct {
405         Name, Hash string
406         Size       uint64
407 }
408
409 func (s *Shell) ObjectGet(path string) (*IpfsObject, error) {
410         var obj IpfsObject
411         if err := s.Request("object/get", path).Exec(context.Background(), &obj); err != nil {
412                 return nil, err
413         }
414         return &obj, nil
415 }
416
417 func (s *Shell) ObjectPut(obj *IpfsObject) (string, error) {
418         var data bytes.Buffer
419         err := json.NewEncoder(&data).Encode(obj)
420         if err != nil {
421                 return "", err
422         }
423
424         fr := files.NewReaderFile(&data)
425         slf := files.NewSliceDirectory([]files.DirEntry{files.FileEntry("", fr)})
426         fileReader := files.NewMultiFileReader(slf, true)
427
428         var out object
429         return out.Hash, s.Request("object/put").
430                 Body(fileReader).
431                 Exec(context.Background(), &out)
432 }
433
434 func (s *Shell) PubSubSubscribe(topic string) (*PubSubSubscription, error) {
435         // connect
436         resp, err := s.Request("pubsub/sub", topic).Send(context.Background())
437         if err != nil {
438                 return nil, err
439         }
440         return newPubSubSubscription(resp), nil
441 }
442
443 func (s *Shell) PubSubPublish(topic, data string) (err error) {
444         resp, err := s.Request("pubsub/pub", topic, data).Send(context.Background())
445         if err != nil {
446                 return err
447         }
448         defer resp.Close()
449         if resp.Error != nil {
450                 return resp.Error
451         }
452         return nil
453 }
454
455 type ObjectStats struct {
456         Hash           string
457         BlockSize      int
458         CumulativeSize int
459         DataSize       int
460         LinksSize      int
461         NumLinks       int
462 }
463
464 // ObjectStat gets stats for the DAG object named by key. It returns
465 // the stats of the requested Object or an error.
466 func (s *Shell) ObjectStat(key string) (*ObjectStats, error) {
467         var stat ObjectStats
468         err := s.Request("object/stat", key).Exec(context.Background(), &stat)
469         if err != nil {
470                 return nil, err
471         }
472         return &stat, nil
473 }
474
475 // ObjectStat gets stats for the DAG object named by key. It returns
476 // the stats of the requested Object or an error.
477 func (s *Shell) StatsBW(ctx context.Context) (*p2pmetrics.Stats, error) {
478         v := &p2pmetrics.Stats{}
479         err := s.Request("stats/bw").Exec(ctx, &v)
480         return v, err
481 }
482
483 type SwarmStreamInfo struct {
484         Protocol string
485 }
486
487 type SwarmConnInfo struct {
488         Addr    string
489         Peer    string
490         Latency string
491         Muxer   string
492         Streams []SwarmStreamInfo
493 }
494
495 type SwarmConnInfos struct {
496         Peers []SwarmConnInfo
497 }
498
499 // SwarmPeers gets all the swarm peers
500 func (s *Shell) SwarmPeers(ctx context.Context) (*SwarmConnInfos, error) {
501         v := &SwarmConnInfos{}
502         err := s.Request("swarm/peers").Exec(ctx, &v)
503         return v, err
504 }
505
506 type swarmConnection struct {
507         Strings []string
508 }
509
510 // SwarmConnect opens a swarm connection to a specific address.
511 func (s *Shell) SwarmConnect(ctx context.Context, addr ...string) error {
512         var conn *swarmConnection
513         err := s.Request("swarm/connect").
514                 Arguments(addr...).
515                 Exec(ctx, &conn)
516         return err
517 }