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

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

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

13
	cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid"
14
	mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash"
Overbool's avatar
Overbool committed
15
	path "gx/ipfs/QmRKuTyCzg7HFBcV1YUhzStroGtJSb8iWgyxfsDCwFhWTS/go-path"
16
	cmds "gx/ipfs/QmSXUokcP4TJpFfqozT69AVAYRtzXVMUjzQVkYX41R9Svs/go-ipfs-cmds"
17
	files "gx/ipfs/QmZMWMvWMVKCbHetJ4RgndbuEF1io2UpUxwQwtNjtYPzSC/go-ipfs-files"
18
	ipld "gx/ipfs/QmdDXJs4axxefSPgK6Y1QhpJWKuDPnGJiqgq4uncb4rFHL/go-ipld-format"
19
	cmdkit "gx/ipfs/Qmde5VP1qUkyQXKCfmEUA7bP64V2HAptbJ7phuPp7jXWwg/go-ipfs-cmdkit"
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 93 94 95 96 97 98 99
		cids := cid.NewSet()
		b := ipld.NewBatch(req.Context, nd.DAG)

		for {
			file, err := req.Files.NextFile()
			if err == io.EOF {
				// Finished the list of files.
				break
			} else if err != nil {
				return err
Łukasz Magiera's avatar
Łukasz Magiera committed
100
			}
101

Steven Allen's avatar
Steven Allen committed
102 103
			nds, err := coredag.ParseInputs(ienc, format, file, mhType, -1)
			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 111
			for _, nd := range nds {
				err := b.Add(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

Steven Allen's avatar
Steven Allen committed
124
		if err := b.Commit(); err != nil {
Overbool's avatar
Overbool committed
125 126
			return err
		}
127

Steven Allen's avatar
Steven Allen committed
128 129 130 131 132 133 134 135 136 137 138 139 140 141
		if dopin {
			defer nd.Blockstore.PinLock().Unlock()

			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
142 143 144 145 146 147 148
	},
	Type: OutputObject{},
	Encoders: cmds.EncoderMap{
		cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *OutputObject) error {
			fmt.Fprintln(w, out.Cid.String())
			return nil
		}),
149 150 151 152
	},
}

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

Overbool's avatar
Overbool committed
169
		p, err := path.ParsePath(req.Arguments[0])
170
		if err != nil {
Overbool's avatar
Overbool committed
171
			return err
172 173
		}

Overbool's avatar
Overbool committed
174
		lastCid, rem, err := nd.Resolver.ResolveToLastNode(req.Context, p)
175
		if err != nil {
Overbool's avatar
Overbool committed
176
			return err
177
		}
Overbool's avatar
Overbool committed
178
		obj, err := nd.DAG.Get(req.Context, lastCid)
179
		if err != nil {
Overbool's avatar
Overbool committed
180
			return err
181 182
		}

183 184 185 186
		var out interface{} = obj
		if len(rem) > 0 {
			final, _, err := obj.Resolve(rem)
			if err != nil {
Overbool's avatar
Overbool committed
187
				return err
188 189 190
			}
			out = final
		}
Overbool's avatar
Overbool committed
191
		return res.Emit(&out)
192 193
	},
}
Łukasz Magiera's avatar
Łukasz Magiera committed
194

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

Overbool's avatar
Overbool committed
212
		p, err := path.ParsePath(req.Arguments[0])
Ł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
		lastCid, rem, err := nd.Resolver.ResolveToLastNode(req.Context, p)
Ł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
		return res.Emit(&ResolveOutput{
223
			Cid:     lastCid,
Łukasz Magiera's avatar
Łukasz Magiera committed
224 225 226
			RemPath: path.Join(rem),
		})
	},
Overbool's avatar
Overbool committed
227 228 229 230 231
	Encoders: cmds.EncoderMap{
		cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *ResolveOutput) error {
			p := out.Cid.String()
			if out.RemPath != "" {
				p = path.Join([]string{p, out.RemPath})
Łukasz Magiera's avatar
Łukasz Magiera committed
232 233
			}

Overbool's avatar
Overbool committed
234 235 236
			fmt.Fprint(w, p)
			return nil
		}),
Łukasz Magiera's avatar
Łukasz Magiera committed
237 238 239
	},
	Type: ResolveOutput{},
}
Jan Winkelmann's avatar
Jan Winkelmann committed
240 241 242 243 244 245 246 247 248 249 250 251 252 253

// copy+pasted from ../commands.go
func unwrapOutput(i interface{}) (interface{}, error) {
	var (
		ch <-chan interface{}
		ok bool
	)

	if ch, ok = i.(<-chan interface{}); !ok {
		return nil, e.TypeErr(ch, i)
	}

	return <-ch, nil
}