master
go 137 lines 3.7 KB
Raw
1 package commands
2
3 import (
4 "context"
5 "errors"
6 "strings"
7 "time"
8
9 core "github.com/ipfs/kubo/core"
10 coreapi "github.com/ipfs/kubo/core/coreapi"
11 loader "github.com/ipfs/kubo/plugin/loader"
12
13 cmds "github.com/ipfs/go-ipfs-cmds"
14 logging "github.com/ipfs/go-log/v2"
15 config "github.com/ipfs/kubo/config"
16 coreiface "github.com/ipfs/kubo/core/coreiface"
17 options "github.com/ipfs/kubo/core/coreiface/options"
18 )
19
20 var log = logging.Logger("command")
21
22 // Context represents request context.
23 type Context struct {
24 ConfigRoot string
25 ReqLog *ReqLog
26
27 Plugins *loader.PluginLoader
28
29 Gateway bool
30 api coreiface.CoreAPI
31 node *core.IpfsNode
32 ConstructNode func() (*core.IpfsNode, error)
33 }
34
35 func (c *Context) GetConfig() (*config.Config, error) {
36 node, err := c.GetNode()
37 if err != nil {
38 return nil, err
39 }
40 return node.Repo.Config()
41 }
42
43 // GetNode returns the node of the current Command execution
44 // context. It may construct it with the provided function.
45 func (c *Context) GetNode() (*core.IpfsNode, error) {
46 var err error
47 if c.node == nil {
48 if c.ConstructNode == nil {
49 return nil, errors.New("nil ConstructNode function")
50 }
51 c.node, err = c.ConstructNode()
52 }
53 return c.node, err
54 }
55
56 // ClearCachedNode clears any cached node, forcing GetNode to construct a new one.
57 //
58 // This method is critical for mitigating racy FX dependency injection behavior
59 // that can occur during daemon startup. The daemon may create multiple IpfsNode
60 // instances during initialization - first an offline node during early init, then
61 // the proper online daemon node. Without clearing the cache, HTTP RPC handlers may
62 // end up using the first (offline) cached node instead of the intended online daemon node.
63 //
64 // This behavior was likely present forever in go-ipfs, but recent changes made it more
65 // prominent and forced us to proactively mitigate FX shortcomings. The daemon calls
66 // this method immediately before setting its ConstructNode function to ensure that
67 // subsequent GetNode() calls use the correct online daemon node rather than any
68 // stale cached offline node from initialization.
69 func (c *Context) ClearCachedNode() {
70 c.node = nil
71 }
72
73 // GetAPI returns CoreAPI instance backed by ipfs node.
74 // It may construct the node with the provided function.
75 func (c *Context) GetAPI() (coreiface.CoreAPI, error) {
76 if c.api == nil {
77 n, err := c.GetNode()
78 if err != nil {
79 return nil, err
80 }
81 fetchBlocks := true
82 if c.Gateway {
83 cfg, err := c.GetConfig()
84 if err != nil {
85 return nil, err
86 }
87 fetchBlocks = !cfg.Gateway.NoFetch
88 }
89
90 c.api, err = coreapi.NewCoreAPI(n, options.Api.FetchBlocks(fetchBlocks))
91 if err != nil {
92 return nil, err
93 }
94 }
95 return c.api, nil
96 }
97
98 // Context returns the node's context.
99 func (c *Context) Context() context.Context {
100 n, err := c.GetNode()
101 if err != nil {
102 log.Debug("error getting node: ", err)
103 return context.Background()
104 }
105
106 return n.Context()
107 }
108
109 // LogRequest adds the passed request to the request log and
110 // returns a function that should be called when the request
111 // lifetime is over.
112 func (c *Context) LogRequest(req *cmds.Request) func() {
113 rle := &ReqLogEntry{
114 StartTime: time.Now(),
115 Active: true,
116 Command: strings.Join(req.Path, "/"),
117 Options: req.Options,
118 Args: req.Arguments,
119 log: c.ReqLog,
120 }
121 c.ReqLog.AddEntry(rle)
122
123 return func() {
124 c.ReqLog.Finish(rle)
125 }
126 }
127
128 // Close cleans up the application state.
129 func (c *Context) Close() {
130 // let's not forget teardown. If a node was initialized, we must close it.
131 // Note that this means the underlying req.Context().Node variable is exposed.
132 // this is gross, and should be changed when we extract out the exec Context.
133 if c.node != nil {
134 log.Info("Shutting down node...")
135 c.node.Close()
136 }
137 }