-
Notifications
You must be signed in to change notification settings - Fork 1
/
unpackingScanner.go
84 lines (74 loc) · 2.98 KB
/
unpackingScanner.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
package util
import (
"context"
api "github.com/polydawn/go-timeless-api"
"github.com/polydawn/go-timeless-api/rio"
"github.com/polydawn/rio/fs"
nilFS "github.com/polydawn/rio/fs/nilfs"
"github.com/polydawn/rio/fs/osfs"
. "github.com/warpfork/go-errcat"
)
// A "scan" is roughly the same as an unpack to /dev/null,
// but takes a single URL, and *doesn't* require a hash.
//
// It can even populate the CAS cache!
//
// However, note that it's an overall intention to make this feature
// usable only very knowingly and with moderate inconvenience -- because you
// *should not* do it in the middle of a script; you should be doing any scans
// *once* and then tracking the resulting references via a release catalog:
// which keeps the overall process more controlled, auditable, and
// well-defined even in the case of untrusted networks.
func CreateScanner(t api.PackType, unpacker unpackFn) rio.ScanFunc {
return func(
ctx context.Context, // Long-running call. Cancellable.
packType api.PackType, // The name of pack format.
filt api.FilesetUnpackFilter, // Optionally: filters we should apply while unpacking.
placementMode rio.PlacementMode, // For scanning only "None" (cache; the default) and "Direct" (don't cache) are valid.
addr api.WarehouseLocation, // The *one* warehouse to fetch from. Must be a monowarehouse (not a CA-mode).
mon rio.Monitor, // Optionally: callbacks for progress monitoring.
) (_ api.WareID, err error) {
if mon.Chan != nil {
defer close(mon.Chan)
}
defer RequireErrorHasCategory(&err, rio.ErrorCategory(""))
// Sanitize arguments.
if packType != t {
return api.WareID{}, Errorf(rio.ErrUsage, "this transmat implementation only supports packtype %q (not %q)", t, packType)
}
if !filt.IsComplete() {
return api.WareID{}, Errorf(rio.ErrUsage, "filters must be completely specified")
}
if placementMode == "" {
placementMode = rio.Placement_None
}
// TODO FUTURE actually support cache
// Dial warehouse.
// Note how this is a subset of the usual accepted warehouses;
// it must be a monowarehouse, not a legit CA storage bucket.
reader, err := PickReader(api.WareID{t, "-"}, []api.WarehouseLocation{addr}, true, mon)
if err != nil {
return api.WareID{}, err
}
defer reader.Close()
// Construct filesystem wrapper to use for all our ops.
// If caching, it's a real fs handle;
// if not, it's a bunch of no-op'ing functions.
var afs fs.FS
switch placementMode {
case rio.Placement_None:
afs = osfs.New(fs.MustAbsolutePath("/nope/nope")) // TODO cache
case rio.Placement_Direct:
afs = nilFS.New()
default:
panic("unreachable")
}
// Extract.
// For once we can actually discard the *prefilter* wareID, since we don't have
// an expected one to assert against.
// TODO: the ware used by the buffer internally will need to be derived from addr
// once caching is supported.
_, unpackedWareID, err := unpacker(ctx, afs, filt, api.WareID{t, "-"}, reader, mon)
return unpackedWareID, err
}
}