Skip to content

Commit

Permalink
REv2: Allow downloads from being prevented by emitting symlinks
Browse files Browse the repository at this point in the history
By default, Bazel's REv2 client downloads all output files of a build
action, which is slow. This can be disabled by using flags like
--remote_download_minimal, but this causes bazel-out/ to be mostly
empty, making the user experience suboptimal.

This change introduces a middle ground: it allows people to develop
virtual file systems (FUSE, NFS, 9p, etc. etc. etc.) that lazily expose
the CAS onto people's systems. It does this by adding a new flag,
--remote_download_symlink_template, that causes Bazel to generate
symlinks that conform to a given template.
  • Loading branch information
EdSchouten committed Sep 3, 2020
1 parent d83fc58 commit 3d08acb
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,18 @@ public ListenableFuture<Void> downloadFile(Path path, Digest digest) throws IOEx
return COMPLETED_SUCCESS;
}

if (!options.remoteDownloadSymlinkTemplate.isEmpty()) {
// Don't actually download files from the CAS. Instead, create a
// symbolic link that points to a location where CAS objects may
// be found. This could, for example, be a FUSE file system.
path.createSymbolicLink(
path.getRelative(
options.remoteDownloadSymlinkTemplate
.replace("{hash}", digest.getHash())
.replace("{size_bytes}", String.valueOf(digest.getSizeBytes()))));
return COMPLETED_SUCCESS;
}

OutputStream out = new LazyFileOutputStream(path);
SettableFuture<Void> outerF = SettableFuture.create();
ListenableFuture<Void> f = cacheProtocol.downloadBlob(digest, out);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,21 @@ public RemoteOutputsStrategyConverter() {
+ " discard the remotely cached values if they don't match the expected value.")
public boolean remoteVerifyDownloads;

@Option(
name = "remote_download_symlink_template",
defaultValue = "",
category = "remote",
documentationCategory = OptionDocumentationCategory.OUTPUT_PARAMETERS,
effectTags = {OptionEffectTag.AFFECTS_OUTPUTS},
help =
"Instead of downloading remote build outputs to the local machine, create symbolic "
+ "links. The target of the symbolic links can be specified in the form of a "
+ "template string. This template string may contain {hash} and {size_bytes} that "
+ "expand to the hash of the object and the size in bytes, respectively. "
+ "These symbolic links may, for example, point to a FUSE file system "
+ "that loads objects from the CAS on demand.")
public String remoteDownloadSymlinkTemplate;

// The below options are not configurable by users, only tests.
// This is part of the effort to reduce the overall number of flags.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1206,6 +1206,33 @@ public void testDownloadEmptyBlobAndFile() throws Exception {
assertThat(file.getFileSize()).isEqualTo(0);
}

@Test
public void testDownloadFileWithSymlinkTemplate() throws Exception {
// Test that when a symlink template is provided, we don't actually download files to disk.
// Instead, a symbolic link should be created that points to a location where the file may
// actually be found. That location could, for example, be backed by a FUSE file system that
// exposes the Content Addressable Storage.

// arrange
final ConcurrentMap<Digest, byte[]> cas = new ConcurrentHashMap<>();

Digest helloDigest = digestUtil.computeAsUtf8("hello-contents");
cas.put(helloDigest, "hello-contents".getBytes(Charsets.UTF_8));

Path file = fs.getPath("/execroot/symlink-to-file");
RemoteOptions options = Options.getDefaults(RemoteOptions.class);
options.remoteDownloadSymlinkTemplate = "/home/alice/cas/{hash}-{size_bytes}";
RemoteCache remoteCache = new InMemoryRemoteCache(cas, options, digestUtil);

// act
Utils.getFromFuture(remoteCache.downloadFile(file, helloDigest));

// assert
assertThat(file.isSymbolicLink()).isTrue();
assertThat(file.readSymbolicLink()).isEqualTo(
PathFragment.create("/home/alice/cas/a378b939ad2e1d470a9a28b34b0e256b189e85cb236766edc1d46ec3b6ca82e5-14"));
}

@Test
public void testDownloadDirectory() throws Exception {
// Test that downloading an output directory works.
Expand Down

0 comments on commit 3d08acb

Please sign in to comment.