dag.go 6.49 KB
Newer Older
1 2 3 4 5
package dagcmd

import (
	"fmt"
	"io"
6
	"math"
7

Overbool's avatar
Overbool committed
8 9 10
	"github.com/ipfs/go-ipfs/core/commands/cmdenv"
	"github.com/ipfs/go-ipfs/core/coredag"
	"github.com/ipfs/go-ipfs/pin"
11

Steven Allen's avatar
Steven Allen committed
12
	path "gx/ipfs/QmNYPETsdAu2uQ1k9q9S1jYEGURaLHV6cbYRSVFVRftpF8/go-path"
Steven Allen's avatar
Steven Allen committed
13
	cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid"
Hector Sanjuan's avatar
Hector Sanjuan committed
14
	cmds "gx/ipfs/QmWGm4AbZEbnmdgVTza52MSNpEmBdFVqzmAysRbjrRyGbH/go-ipfs-cmds"
Łukasz Magiera's avatar
Łukasz Magiera committed
15
	files "gx/ipfs/QmXWZCd8jfaHmt4UDSnjKmGcrQMw95bDGWqEeVLVJjoANX/go-ipfs-files"
Steven Allen's avatar
Steven Allen committed
16
	ipld "gx/ipfs/QmcKKBwfz6FyQdHR2jsXrrF6XeSBXYL86anmWNewpFpoF5/go-ipld-format"
17
	cidenc "gx/ipfs/QmdPQx9fvN5ExVwMhRmh7YpCQJzJrFhd1AjVBwJmRMFJeX/go-cidutil/cidenc"
18
	cmdkit "gx/ipfs/Qmde5VP1qUkyQXKCfmEUA7bP64V2HAptbJ7phuPp7jXWwg/go-ipfs-cmdkit"
Steven Allen's avatar
Steven Allen committed
19
	mh "gx/ipfs/QmerPMzPk1mJVowm8KgmoknWa4yCYvvugMPsgWmDNUvDLW/go-multihash"
20 21 22
)

var DagCmd = &cmds.Command{
Jan Winkelmann's avatar
Jan Winkelmann committed
23
	Helptext: cmdkit.HelpText{
Jeromy's avatar
Jeromy committed
24 25 26 27 28 29 30
		Tagline: "Interact with ipld dag objects.",
		ShortDescription: `
'ipfs dag' is used for creating and manipulating dag objects.

This subcommand is currently an experimental feature, but it is intended
to deprecate and replace the existing 'ipfs object' command moving forward.
		`,
31 32
	},
	Subcommands: map[string]*cmds.Command{
Łukasz Magiera's avatar
Łukasz Magiera committed
33 34 35
		"put":     DagPutCmd,
		"get":     DagGetCmd,
		"resolve": DagResolveCmd,
36 37 38
	},
}

Łukasz Magiera's avatar
Łukasz Magiera committed
39
// OutputObject is the output type of 'dag put' command
40
type OutputObject struct {
41
	Cid cid.Cid
42 43
}

Łukasz Magiera's avatar
Łukasz Magiera committed
44
// ResolveOutput is the output type of 'dag resolve' command
Łukasz Magiera's avatar
Łukasz Magiera committed
45
type ResolveOutput struct {
46
	Cid     cid.Cid
Łukasz Magiera's avatar
Łukasz Magiera committed
47 48 49
	RemPath string
}

50
var DagPutCmd = &cmds.Command{
Jan Winkelmann's avatar
Jan Winkelmann committed
51
	Helptext: cmdkit.HelpText{
52
		Tagline: "Add a dag node to ipfs.",
Jeromy's avatar
Jeromy committed
53 54 55 56
		ShortDescription: `
'ipfs dag put' accepts input from a file or stdin and parses it
into an object of the specified format.
`,
57
	},
Jan Winkelmann's avatar
Jan Winkelmann committed
58 59
	Arguments: []cmdkit.Argument{
		cmdkit.FileArg("object data", true, true, "The object to put").EnableStdin(),
60
	},
Jan Winkelmann's avatar
Jan Winkelmann committed
61
	Options: []cmdkit.Option{
62 63
		cmdkit.StringOption("format", "f", "Format that the object will be added as.").WithDefault("cbor"),
		cmdkit.StringOption("input-enc", "Format that the input object will be.").WithDefault("json"),
64
		cmdkit.BoolOption("pin", "Pin this object when adding."),
65
		cmdkit.StringOption("hash", "Hash function to use").WithDefault(""),
66
	},
Overbool's avatar
Overbool committed
67 68
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
		nd, err := cmdenv.GetNode(env)
69
		if err != nil {
Overbool's avatar
Overbool committed
70
			return err
71 72
		}

Overbool's avatar
Overbool committed
73 74 75 76
		ienc, _ := req.Options["input-enc"].(string)
		format, _ := req.Options["format"].(string)
		hash, _ := req.Options["hash"].(string)
		dopin, _ := req.Options["pin"].(bool)
77

78 79 80 81 82 83 84 85
		// mhType tells inputParser which hash should be used. MaxUint64 means 'use
		// default hash' (sha256 for cbor, sha1 for git..)
		mhType := uint64(math.MaxUint64)

		if hash != "" {
			var ok bool
			mhType, ok = mh.Names[hash]
			if !ok {
Overbool's avatar
Overbool committed
86
				return fmt.Errorf("%s in not a valid multihash name", hash)
87 88 89
			}
		}

Steven Allen's avatar
Steven Allen committed
90 91 92
		cids := cid.NewSet()
		b := ipld.NewBatch(req.Context, nd.DAG)

Steven Allen's avatar
Steven Allen committed
93 94 95 96
		if dopin {
			defer nd.Blockstore.PinLock().Unlock()
		}

97
		it := req.Files.Entries()
98
		for it.Next() {
99 100
			file := files.FileFromEntry(it)
			if file == nil {
101
				return fmt.Errorf("expected a regular file")
Łukasz Magiera's avatar
Łukasz Magiera committed
102
			}
103
			nds, err := coredag.ParseInputs(ienc, format, file, mhType, -1)
Steven Allen's avatar
Steven Allen committed
104
			if err != nil {
Łukasz Magiera's avatar
Łukasz Magiera committed
105 106
				return err
			}
Steven Allen's avatar
Steven Allen committed
107 108 109
			if len(nds) == 0 {
				return fmt.Errorf("no node returned from ParseInputs")
			}
110

Steven Allen's avatar
Steven Allen committed
111
			for _, nd := range nds {
Hector Sanjuan's avatar
Hector Sanjuan committed
112
				err := b.Add(req.Context, nd)
Łukasz Magiera's avatar
Łukasz Magiera committed
113 114 115
				if err != nil {
					return err
				}
Jeromy's avatar
Jeromy committed
116
			}
117

Steven Allen's avatar
Steven Allen committed
118 119 120 121 122
			cid := nds[0].Cid()
			cids.Add(cid)
			if err := res.Emit(&OutputObject{Cid: cid}); err != nil {
				return err
			}
123
		}
124
		if it.Err() != nil {
125
			return it.Err()
126
		}
127

Steven Allen's avatar
Steven Allen committed
128
		if err := b.Commit(); err != nil {
Overbool's avatar
Overbool committed
129 130
			return err
		}
131

Steven Allen's avatar
Steven Allen committed
132 133 134 135 136 137 138 139 140 141 142 143
		if dopin {
			cids.ForEach(func(c cid.Cid) error {
				nd.Pinning.PinWithMode(c, pin.Recursive)
				return nil
			})

			err := nd.Pinning.Flush()
			if err != nil {
				return err
			}
		}
		return nil
Overbool's avatar
Overbool committed
144 145 146 147
	},
	Type: OutputObject{},
	Encoders: cmds.EncoderMap{
		cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *OutputObject) error {
148 149 150 151 152
			enc, err := cmdenv.GetLowLevelCidEncoder(req)
			if err != nil {
				return err
			}
			fmt.Fprintln(w, enc.Encode(out.Cid))
Overbool's avatar
Overbool committed
153 154
			return nil
		}),
155 156 157 158
	},
}

var DagGetCmd = &cmds.Command{
Jan Winkelmann's avatar
Jan Winkelmann committed
159
	Helptext: cmdkit.HelpText{
160
		Tagline: "Get a dag node from ipfs.",
Jeromy's avatar
Jeromy committed
161
		ShortDescription: `
Łukasz Magiera's avatar
Łukasz Magiera committed
162
'ipfs dag get' fetches a dag node from ipfs and prints it out in the specified
163
format.
Jeromy's avatar
Jeromy committed
164
`,
165
	},
Jan Winkelmann's avatar
Jan Winkelmann committed
166 167
	Arguments: []cmdkit.Argument{
		cmdkit.StringArg("ref", true, false, "The object to get").EnableStdin(),
168
	},
Overbool's avatar
Overbool committed
169 170
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
		nd, err := cmdenv.GetNode(env)
171
		if err != nil {
Overbool's avatar
Overbool committed
172
			return err
173 174
		}

Overbool's avatar
Overbool committed
175
		p, err := path.ParsePath(req.Arguments[0])
176
		if err != nil {
Overbool's avatar
Overbool committed
177
			return err
178 179
		}

Overbool's avatar
Overbool committed
180
		lastCid, rem, err := nd.Resolver.ResolveToLastNode(req.Context, p)
181
		if err != nil {
Overbool's avatar
Overbool committed
182
			return err
183
		}
Overbool's avatar
Overbool committed
184
		obj, err := nd.DAG.Get(req.Context, lastCid)
185
		if err != nil {
Overbool's avatar
Overbool committed
186
			return err
187 188
		}

189 190 191 192
		var out interface{} = obj
		if len(rem) > 0 {
			final, _, err := obj.Resolve(rem)
			if err != nil {
Overbool's avatar
Overbool committed
193
				return err
194 195 196
			}
			out = final
		}
197
		return cmds.EmitOnce(res, &out)
198 199
	},
}
Łukasz Magiera's avatar
Łukasz Magiera committed
200

Łukasz Magiera's avatar
Łukasz Magiera committed
201
// DagResolveCmd returns address of highest block within a path and a path remainder
Łukasz Magiera's avatar
Łukasz Magiera committed
202
var DagResolveCmd = &cmds.Command{
Jan Winkelmann's avatar
Jan Winkelmann committed
203
	Helptext: cmdkit.HelpText{
Łukasz Magiera's avatar
Łukasz Magiera committed
204 205 206 207 208
		Tagline: "Resolve ipld block",
		ShortDescription: `
'ipfs dag resolve' fetches a dag node from ipfs, prints it's address and remaining path.
`,
	},
Jan Winkelmann's avatar
Jan Winkelmann committed
209 210
	Arguments: []cmdkit.Argument{
		cmdkit.StringArg("ref", true, false, "The path to resolve").EnableStdin(),
Łukasz Magiera's avatar
Łukasz Magiera committed
211
	},
Overbool's avatar
Overbool committed
212 213
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
		nd, err := cmdenv.GetNode(env)
Łukasz Magiera's avatar
Łukasz Magiera committed
214
		if err != nil {
Overbool's avatar
Overbool committed
215
			return err
Łukasz Magiera's avatar
Łukasz Magiera committed
216 217
		}

Overbool's avatar
Overbool committed
218
		p, err := path.ParsePath(req.Arguments[0])
Łukasz Magiera's avatar
Łukasz Magiera committed
219
		if err != nil {
Overbool's avatar
Overbool committed
220
			return err
Łukasz Magiera's avatar
Łukasz Magiera committed
221 222
		}

Overbool's avatar
Overbool committed
223
		lastCid, rem, err := nd.Resolver.ResolveToLastNode(req.Context, p)
Łukasz Magiera's avatar
Łukasz Magiera committed
224
		if err != nil {
Overbool's avatar
Overbool committed
225
			return err
Łukasz Magiera's avatar
Łukasz Magiera committed
226 227
		}

228
		return cmds.EmitOnce(res, &ResolveOutput{
229
			Cid:     lastCid,
Łukasz Magiera's avatar
Łukasz Magiera committed
230 231 232
			RemPath: path.Join(rem),
		})
	},
Overbool's avatar
Overbool committed
233 234
	Encoders: cmds.EncoderMap{
		cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *ResolveOutput) error {
235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252
			var (
				enc cidenc.Encoder
				err error
			)
			switch {
			case !cmdenv.CidBaseDefined(req):
				// Not specified, check the path.
				enc, err = cmdenv.CidEncoderFromPath(req.Arguments[0])
				if err == nil {
					break
				}
				// Nope, fallback on the default.
				fallthrough
			default:
				enc, err = cmdenv.GetLowLevelCidEncoder(req)
				if err != nil {
					return err
				}
253 254
			}
			p := enc.Encode(out.Cid)
Overbool's avatar
Overbool committed
255 256
			if out.RemPath != "" {
				p = path.Join([]string{p, out.RemPath})
Łukasz Magiera's avatar
Łukasz Magiera committed
257 258
			}

Overbool's avatar
Overbool committed
259 260 261
			fmt.Fprint(w, p)
			return nil
		}),
Łukasz Magiera's avatar
Łukasz Magiera committed
262 263 264
	},
	Type: ResolveOutput{},
}