From b08b7b575fb8abf3848e2315d903f752b3f65924 Mon Sep 17 00:00:00 2001 From: Tom Clegg Date: Wed, 26 Jun 2024 17:37:53 -0400 Subject: [PATCH] 21701: Accept "manifest_text" and "current" replace_files sources. Arvados-DCO-1.1-Signed-off-by: Tom Clegg --- lib/controller/localdb/collection.go | 54 +++++++++++++++++++++++----- 1 file changed, 45 insertions(+), 9 deletions(-) diff --git a/lib/controller/localdb/collection.go b/lib/controller/localdb/collection.go index bfc5c8a426..a661c30560 100644 --- a/lib/controller/localdb/collection.go +++ b/lib/controller/localdb/collection.go @@ -152,9 +152,8 @@ func (conn *Conn) lockUUID(ctx context.Context, uuid string) error { func (conn *Conn) applyReplaceFilesOption(ctx context.Context, fromUUID string, attrs map[string]interface{}, replaceFiles map[string]string) (map[string]interface{}, error) { if len(replaceFiles) == 0 { return attrs, nil - } else if mtxt, ok := attrs["manifest_text"].(string); ok && len(mtxt) > 0 { - return nil, httpserver.Errorf(http.StatusBadRequest, "ambiguous request: both 'replace_files' and attrs['manifest_text'] values provided") } + providedManifestText, _ := attrs["manifest_text"].(string) // Load the current collection (if any) and set up an // in-memory filesystem. @@ -216,6 +215,21 @@ func (conn *Conn) applyReplaceFilesOption(ctx context.Context, fromUUID string, } } + current := make(map[string]*arvados.Subtree) + // Check whether any sources are "current/...", and if so, + // populate current with the relevant snapshot. Doing this + // ahead of time, before making any modifications to dstfs + // below, ensures that even instructions like {/a: current/b, + // b: current/a} will be handled correctly. + for _, src := range replaceFiles { + if strings.HasPrefix(src, "current/") && current[src] == nil { + current[src], err = arvados.Snapshot(dstfs, src[8:]) + if err != nil { + return nil, fmt.Errorf("%s: %w", src, err) + } + } + } + var srcidloaded string var srcfs arvados.FileSystem // Apply the requested replacements. @@ -234,15 +248,33 @@ func (conn *Conn) applyReplaceFilesOption(ctx context.Context, fromUUID string, } continue } + var snap *arvados.Subtree srcspec := strings.SplitN(src, "/", 2) srcid, srcpath := srcspec[0], "/" - if !arvadosclient.PDHMatch(srcid) { - return nil, httpserver.Errorf(http.StatusBadRequest, "invalid source %q for replace_files[%q]: must be \"\" or \"PDH\" or \"PDH/path\"", src, dst) - } if len(srcspec) == 2 && srcspec[1] != "" { srcpath = srcspec[1] } - if srcidloaded != srcid { + switch { + case srcid == "current": + snap = current[src] + if snap == nil { + return nil, fmt.Errorf("internal error: current[%s] == nil", src) + } + case srcid == "manifest_text": + if srcidloaded == srcid { + break + } + srcfs = nil + srccoll := &arvados.Collection{ManifestText: providedManifestText} + srcfs, err = srccoll.FileSystem(&arvados.StubClient{}, &arvados.StubClient{}) + if err != nil { + return nil, err + } + srcidloaded = srcid + case arvadosclient.PDHMatch(srcid): + if srcidloaded == srcid { + break + } srcfs = nil srccoll, err := conn.CollectionGet(ctx, arvados.GetOptions{UUID: srcid}) if err != nil { @@ -256,10 +288,14 @@ func (conn *Conn) applyReplaceFilesOption(ctx context.Context, fromUUID string, return nil, err } srcidloaded = srcid + default: + return nil, httpserver.Errorf(http.StatusBadRequest, "invalid source %q for replace_files[%q]: must be \"\" or \"SRC\" or \"SRC/path\" where SRC is \"current\", \"manifest_text\", or a portable data hash", src, dst) } - snap, err := arvados.Snapshot(srcfs, srcpath) - if err != nil { - return nil, httpserver.Errorf(http.StatusBadRequest, "error getting snapshot of %q from %q: %w", srcpath, srcid, err) + if snap == nil { + snap, err = arvados.Snapshot(srcfs, srcpath) + if err != nil { + return nil, httpserver.Errorf(http.StatusBadRequest, "error getting snapshot of %q from %q: %w", srcpath, srcid, err) + } } // Create intermediate dirs, in case dst is // "newdir1/newdir2/dst". -- 2.39.5