dag.go 6.21 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"
15
	files "gx/ipfs/QmXWZCd8jfaHmt4UDSnjKmGcrQMw95bDGWqEeVLVJjoANX/go-ipfs-files"
Steven Allen's avatar
Steven Allen committed
16
	ipld "gx/ipfs/QmcKKBwfz6FyQdHR2jsXrrF6XeSBXYL86anmWNewpFpoF5/go-ipld-format"
17
	cmdkit "gx/ipfs/Qmde5VP1qUkyQXKCfmEUA7bP64V2HAptbJ7phuPp7jXWwg/go-ipfs-cmdkit"
Steven Allen's avatar
Steven Allen committed
18
	mh "gx/ipfs/QmerPMzPk1mJVowm8KgmoknWa4yCYvvugMPsgWmDNUvDLW/go-multihash"
19 20 21
)

var DagCmd = &cmds.Command{
Jan Winkelmann's avatar
Jan Winkelmann committed
22
	Helptext: cmdkit.HelpText{
Jeromy's avatar
Jeromy committed
23 24 25 26 27 28 29
		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.
		`,
30 31
	},
	Subcommands: map[string]*cmds.Command{
Łukasz Magiera's avatar
Łukasz Magiera committed
32 33 34
		"put":     DagPutCmd,
		"get":     DagGetCmd,
		"resolve": DagResolveCmd,
35 36 37
	},
}

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

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

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

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

77 78 79 80 81 82 83 84
		// 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
85
				return fmt.Errorf("%s in not a valid multihash name", hash)
86 87 88
			}
		}

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

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

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

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

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

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

Steven Allen's avatar
Steven Allen committed
131 132 133 134 135 136 137 138 139 140 141 142
		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
143 144 145 146
	},
	Type: OutputObject{},
	Encoders: cmds.EncoderMap{
		cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *OutputObject) error {
147 148 149 150 151
			enc, err := cmdenv.GetLowLevelCidEncoder(req)
			if err != nil {
				return err
			}
			fmt.Fprintln(w, enc.Encode(out.Cid))
Overbool's avatar
Overbool committed
152 153
			return nil
		}),
154 155 156 157
	},
}

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

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

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

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

Łukasz Magiera's avatar
Łukasz Magiera committed
200
// DagResolveCmd returns address of highest block within a path and a path remainder
Łukasz Magiera's avatar
Łukasz Magiera committed
201
var DagResolveCmd = &cmds.Command{
Jan Winkelmann's avatar
Jan Winkelmann committed
202
	Helptext: cmdkit.HelpText{
Łukasz Magiera's avatar
Łukasz Magiera committed
203 204 205 206 207
		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
208 209
	Arguments: []cmdkit.Argument{
		cmdkit.StringArg("ref", true, false, "The path to resolve").EnableStdin(),
Łukasz Magiera's avatar
Łukasz Magiera committed
210
	},
Overbool's avatar
Overbool committed
211 212
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
		nd, err := cmdenv.GetNode(env)
Łukasz Magiera's avatar
Łukasz Magiera committed
213
		if err != nil {
Overbool's avatar
Overbool committed
214
			return err
Łukasz Magiera's avatar
Łukasz Magiera committed
215 216
		}

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

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

227
		return cmds.EmitOnce(res, &ResolveOutput{
228
			Cid:     lastCid,
Łukasz Magiera's avatar
Łukasz Magiera committed
229 230 231
			RemPath: path.Join(rem),
		})
	},
Overbool's avatar
Overbool committed
232 233
	Encoders: cmds.EncoderMap{
		cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *ResolveOutput) error {
234 235 236 237 238 239 240 241
			enc, err := cmdenv.GetLowLevelCidEncoder(req)
			if err != nil {
				return err
			}
			if !cmdenv.CidBaseDefined(req) {
				enc, _ = cmdenv.CidEncoderFromPath(enc, req.Arguments[0])
			}
			p := enc.Encode(out.Cid)
Overbool's avatar
Overbool committed
242 243
			if out.RemPath != "" {
				p = path.Join([]string{p, out.RemPath})
Łukasz Magiera's avatar
Łukasz Magiera committed
244 245
			}

Overbool's avatar
Overbool committed
246 247 248
			fmt.Fprint(w, p)
			return nil
		}),
Łukasz Magiera's avatar
Łukasz Magiera committed
249 250 251
	},
	Type: ResolveOutput{},
}