Add an optional allowlist for external network in tests.
PiperOrigin-RevId: 387118640
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/Allowlist.java b/src/main/java/com/google/devtools/build/lib/analysis/Allowlist.java
index b6ff313..0f50913 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/Allowlist.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/Allowlist.java
@@ -14,6 +14,7 @@
package com.google.devtools.build.lib.analysis;
+import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.devtools.build.lib.packages.Attribute.attr;
import static com.google.devtools.build.lib.packages.BuildType.LABEL;
import static java.util.Objects.requireNonNull;
@@ -25,6 +26,7 @@
import com.google.devtools.build.lib.collect.nestedset.NestedSet;
import com.google.devtools.build.lib.packages.Attribute;
import com.google.devtools.build.lib.packages.PackageSpecification.PackageGroupContents;
+import javax.annotation.Nullable;
/**
* Class used for implementing allowlists using package groups.
@@ -97,6 +99,15 @@
public static PackageSpecificationProvider fetchPackageSpecificationProvider(
RuleContext ruleContext, String allowlistName) {
+ return checkNotNull(
+ fetchPackageSpecificationProviderOrNull(ruleContext, allowlistName),
+ "Allowlist argument for %s not found",
+ allowlistName);
+ }
+
+ @Nullable
+ public static PackageSpecificationProvider fetchPackageSpecificationProviderOrNull(
+ RuleContext ruleContext, String allowlistName) {
for (String attributeName : getAttributeNameFromAllowlistName(allowlistName)) {
if (!ruleContext.isAttrDefined(attributeName, LABEL)) {
continue;
@@ -107,7 +118,7 @@
packageGroup.getProvider(PackageSpecificationProvider.class);
return requireNonNull(packageSpecificationProvider, packageGroup.getLabel().toString());
}
- throw new AssertionError();
+ return null;
}
/**
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/BaseRuleClasses.java b/src/main/java/com/google/devtools/build/lib/analysis/BaseRuleClasses.java
index bba6923..e804edb 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/BaseRuleClasses.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/BaseRuleClasses.java
@@ -186,7 +186,7 @@
public static final class TestBaseRule implements RuleDefinition {
@Override
public RuleClass build(RuleClass.Builder builder, RuleDefinitionEnvironment env) {
- return builder
+ builder
.addExecGroup(TEST_RUNNER_EXEC_GROUP)
.requiresConfigurationFragments(TestConfiguration.class)
// TestConfiguration only needed to create TestAction and TestProvider
@@ -259,8 +259,15 @@
coverageReportGeneratorAttribute(
env.getToolsLabel(DEFAULT_COVERAGE_REPORT_GENERATOR_VALUE))))
// The target itself and run_under both run on the same machine.
- .add(attr(":run_under", LABEL).value(RUN_UNDER).skipPrereqValidatorCheck())
- .build();
+ .add(attr(":run_under", LABEL).value(RUN_UNDER).skipPrereqValidatorCheck());
+
+ env.getNetworkAllowlistForTests()
+ .ifPresent(
+ label ->
+ builder.add(
+ Allowlist.getAttributeFromAllowlistName("$network_allowlist").value(label)));
+
+ return builder.build();
}
@Override
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredRuleClassProvider.java b/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredRuleClassProvider.java
index 0c93f7c..3dffb6a 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredRuleClassProvider.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredRuleClassProvider.java
@@ -74,6 +74,7 @@
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import java.util.Set;
import java.util.TreeSet;
import java.util.zip.ZipEntry;
@@ -171,6 +172,9 @@
private ThirdPartyLicenseExistencePolicy thirdPartyLicenseExistencePolicy =
ThirdPartyLicenseExistencePolicy.USER_CONTROLLABLE;
+ // TODO(b/192694287): Remove once we migrate all tests from the allowlist
+ @Nullable private Label networkAllowlistForTests;
+
public Builder addWorkspaceFilePrefix(String contents) {
defaultWorkspaceFilePrefix.append(contents);
return this;
@@ -572,6 +576,16 @@
public String getToolsRepository() {
return toolsRepository;
}
+
+ public Builder setNetworkAllowlistForTests(Label allowlist) {
+ networkAllowlistForTests = allowlist;
+ return this;
+ }
+
+ @Override
+ public Optional<Label> getNetworkAllowlistForTests() {
+ return Optional.ofNullable(networkAllowlistForTests);
+ }
}
/** Default content that should be added at the beginning of the WORKSPACE file. */
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/RuleDefinitionEnvironment.java b/src/main/java/com/google/devtools/build/lib/analysis/RuleDefinitionEnvironment.java
index f9f646b..6db1e4a 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/RuleDefinitionEnvironment.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/RuleDefinitionEnvironment.java
@@ -15,6 +15,7 @@
package com.google.devtools.build.lib.analysis;
import com.google.devtools.build.lib.cmdline.Label;
+import java.util.Optional;
/**
* A minimal context available during rule definition, for both native and starlark rules.
@@ -33,7 +34,16 @@
* <p>TODO(brandjon,twigg): Require override to handle repositoryMapping? Note that
* Label.parseAbsoluteUnchecked itself is deprecated because of repositoryMapping!
*/
- public default Label getToolsLabel(String labelValue) {
+ default Label getToolsLabel(String labelValue) {
return Label.parseAbsoluteUnchecked(getToolsRepository() + labelValue);
}
+
+ /**
+ * Returns a label for network allowlist for tests if one should be added.
+ *
+ * <p>TODO(b/192694287): Remove once we migrate all tests from the allowlist.
+ */
+ default Optional<Label> getNetworkAllowlistForTests() {
+ return Optional.empty();
+ }
}
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/starlark/StarlarkRuleClassFunctions.java b/src/main/java/com/google/devtools/build/lib/analysis/starlark/StarlarkRuleClassFunctions.java
index 9349cbc..f2549c0 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/starlark/StarlarkRuleClassFunctions.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/starlark/StarlarkRuleClassFunctions.java
@@ -34,6 +34,7 @@
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.analysis.Allowlist;
import com.google.devtools.build.lib.analysis.BaseRuleClasses;
import com.google.devtools.build.lib.analysis.RuleDefinitionEnvironment;
import com.google.devtools.build.lib.analysis.TemplateVariableInfo;
@@ -172,87 +173,95 @@
.build();
/** Parent rule class for test Starlark rules. */
- public static final RuleClass getTestBaseRule(RuleDefinitionEnvironment env) {
+ public static RuleClass getTestBaseRule(RuleDefinitionEnvironment env) {
String toolsRepository = env.getToolsRepository();
- return new RuleClass.Builder("$test_base_rule", RuleClassType.ABSTRACT, true, baseRule)
- .requiresConfigurationFragments(TestConfiguration.class)
- // TestConfiguration only needed to create TestAction and TestProvider
- // Only necessary at top-level and can be skipped if trimmed.
- .setMissingFragmentPolicy(TestConfiguration.class, MissingFragmentPolicy.IGNORE)
- .add(
- attr("size", STRING)
- .value("medium")
- .taggable()
- .nonconfigurable("used in loading phase rule validation logic"))
- .add(
- attr("timeout", STRING)
- .taggable()
- .nonconfigurable("policy decision: should be consistent across configurations")
- .value(TIMEOUT_DEFAULT))
- .add(
- attr("flaky", BOOLEAN)
- .value(false)
- .taggable()
- .nonconfigurable("taggable - called in Rule.getRuleTags"))
- .add(attr("shard_count", INTEGER).value(StarlarkInt.of(-1)))
- .add(
- attr("local", BOOLEAN)
- .value(false)
- .taggable()
- .nonconfigurable(
- "policy decision: this should be consistent across configurations"))
- .add(attr("args", STRING_LIST))
- // Input files for every test action
- .add(
- attr("$test_wrapper", LABEL)
- .cfg(HostTransition.createFactory())
- .singleArtifact()
- .value(labelCache.get(toolsRepository + "//tools/test:test_wrapper")))
- .add(
- attr("$xml_writer", LABEL)
- .cfg(HostTransition.createFactory())
- .singleArtifact()
- .value(labelCache.get(toolsRepository + "//tools/test:xml_writer")))
- .add(
- attr("$test_runtime", LABEL_LIST)
- .cfg(HostTransition.createFactory())
- // Getting this default value through the getTestRuntimeLabelList helper ensures we
- // reuse the same ImmutableList<Label> instance for each $test_runtime attr.
- .value(getTestRuntimeLabelList(env)))
- .add(
- attr("$test_setup_script", LABEL)
- .cfg(HostTransition.createFactory())
- .singleArtifact()
- .value(labelCache.get(toolsRepository + "//tools/test:test_setup")))
- .add(
- attr("$xml_generator_script", LABEL)
- .cfg(HostTransition.createFactory())
- .singleArtifact()
- .value(labelCache.get(toolsRepository + "//tools/test:test_xml_generator")))
- .add(
- attr("$collect_coverage_script", LABEL)
- .cfg(HostTransition.createFactory())
- .singleArtifact()
- .value(labelCache.get(toolsRepository + "//tools/test:collect_coverage")))
- // Input files for test actions collecting code coverage
- .add(
- attr(":coverage_support", LABEL)
- .cfg(HostTransition.createFactory())
- .value(
- BaseRuleClasses.coverageSupportAttribute(
- labelCache.get(
- toolsRepository + BaseRuleClasses.DEFAULT_COVERAGE_SUPPORT_VALUE))))
- // Used in the one-per-build coverage report generation action.
- .add(
- attr(":coverage_report_generator", LABEL)
- .cfg(HostTransition.createFactory())
- .value(
- BaseRuleClasses.coverageReportGeneratorAttribute(
- labelCache.get(
- toolsRepository
- + BaseRuleClasses.DEFAULT_COVERAGE_REPORT_GENERATOR_VALUE))))
- .add(attr(":run_under", LABEL).value(RUN_UNDER))
- .build();
+ RuleClass.Builder builder =
+ new RuleClass.Builder("$test_base_rule", RuleClassType.ABSTRACT, true, baseRule)
+ .requiresConfigurationFragments(TestConfiguration.class)
+ // TestConfiguration only needed to create TestAction and TestProvider
+ // Only necessary at top-level and can be skipped if trimmed.
+ .setMissingFragmentPolicy(TestConfiguration.class, MissingFragmentPolicy.IGNORE)
+ .add(
+ attr("size", STRING)
+ .value("medium")
+ .taggable()
+ .nonconfigurable("used in loading phase rule validation logic"))
+ .add(
+ attr("timeout", STRING)
+ .taggable()
+ .nonconfigurable("policy decision: should be consistent across configurations")
+ .value(TIMEOUT_DEFAULT))
+ .add(
+ attr("flaky", BOOLEAN)
+ .value(false)
+ .taggable()
+ .nonconfigurable("taggable - called in Rule.getRuleTags"))
+ .add(attr("shard_count", INTEGER).value(StarlarkInt.of(-1)))
+ .add(
+ attr("local", BOOLEAN)
+ .value(false)
+ .taggable()
+ .nonconfigurable(
+ "policy decision: this should be consistent across configurations"))
+ .add(attr("args", STRING_LIST))
+ // Input files for every test action
+ .add(
+ attr("$test_wrapper", LABEL)
+ .cfg(HostTransition.createFactory())
+ .singleArtifact()
+ .value(labelCache.get(toolsRepository + "//tools/test:test_wrapper")))
+ .add(
+ attr("$xml_writer", LABEL)
+ .cfg(HostTransition.createFactory())
+ .singleArtifact()
+ .value(labelCache.get(toolsRepository + "//tools/test:xml_writer")))
+ .add(
+ attr("$test_runtime", LABEL_LIST)
+ .cfg(HostTransition.createFactory())
+ // Getting this default value through the getTestRuntimeLabelList helper ensures
+ // we reuse the same ImmutableList<Label> instance for each $test_runtime attr.
+ .value(getTestRuntimeLabelList(env)))
+ .add(
+ attr("$test_setup_script", LABEL)
+ .cfg(HostTransition.createFactory())
+ .singleArtifact()
+ .value(labelCache.get(toolsRepository + "//tools/test:test_setup")))
+ .add(
+ attr("$xml_generator_script", LABEL)
+ .cfg(HostTransition.createFactory())
+ .singleArtifact()
+ .value(labelCache.get(toolsRepository + "//tools/test:test_xml_generator")))
+ .add(
+ attr("$collect_coverage_script", LABEL)
+ .cfg(HostTransition.createFactory())
+ .singleArtifact()
+ .value(labelCache.get(toolsRepository + "//tools/test:collect_coverage")))
+ // Input files for test actions collecting code coverage
+ .add(
+ attr(":coverage_support", LABEL)
+ .cfg(HostTransition.createFactory())
+ .value(
+ BaseRuleClasses.coverageSupportAttribute(
+ labelCache.get(
+ toolsRepository + BaseRuleClasses.DEFAULT_COVERAGE_SUPPORT_VALUE))))
+ // Used in the one-per-build coverage report generation action.
+ .add(
+ attr(":coverage_report_generator", LABEL)
+ .cfg(HostTransition.createFactory())
+ .value(
+ BaseRuleClasses.coverageReportGeneratorAttribute(
+ labelCache.get(
+ toolsRepository
+ + BaseRuleClasses.DEFAULT_COVERAGE_REPORT_GENERATOR_VALUE))))
+ .add(attr(":run_under", LABEL).value(RUN_UNDER));
+
+ env.getNetworkAllowlistForTests()
+ .ifPresent(
+ label ->
+ builder.add(
+ Allowlist.getAttributeFromAllowlistName("$network_allowlist").value(label)));
+
+ return builder.build();
}
@Override
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/test/TestActionBuilder.java b/src/main/java/com/google/devtools/build/lib/analysis/test/TestActionBuilder.java
index ecb2f35..c63c53c 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/test/TestActionBuilder.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/test/TestActionBuilder.java
@@ -17,6 +17,7 @@
import static com.google.devtools.build.lib.analysis.BaseRuleClasses.TEST_RUNNER_EXEC_GROUP;
import static com.google.devtools.build.lib.packages.BuildType.LABEL;
+import com.google.common.base.MoreObjects;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
@@ -25,9 +26,11 @@
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.actions.ArtifactRoot;
import com.google.devtools.build.lib.actions.RunfilesSupplier;
+import com.google.devtools.build.lib.analysis.Allowlist;
import com.google.devtools.build.lib.analysis.AnalysisEnvironment;
import com.google.devtools.build.lib.analysis.FileProvider;
import com.google.devtools.build.lib.analysis.FilesToRunProvider;
+import com.google.devtools.build.lib.analysis.PackageSpecificationProvider;
import com.google.devtools.build.lib.analysis.PrerequisiteArtifacts;
import com.google.devtools.build.lib.analysis.RuleContext;
import com.google.devtools.build.lib.analysis.Runfiles;
@@ -43,6 +46,8 @@
import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
import com.google.devtools.build.lib.collect.nestedset.Order;
import com.google.devtools.build.lib.packages.TestTimeout;
+import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec.VisibleForSerialization;
+import com.google.devtools.build.lib.skyframe.serialization.autocodec.SerializationConstant;
import com.google.devtools.build.lib.util.OS;
import com.google.devtools.build.lib.util.Pair;
import com.google.devtools.build.lib.vfs.PathFragment;
@@ -66,6 +71,10 @@
private static final String COVERAGE_REPORTED_TO_ACTUAL_SOURCES_FILE =
"COVERAGE_REPORTED_TO_ACTUAL_SOURCES_FILE";
+ @VisibleForSerialization @SerializationConstant
+ static final PackageSpecificationProvider EMPTY_PACKAGES_PROVIDER =
+ () -> NestedSetBuilder.emptySet(Order.STABLE_ORDER);
+
private final RuleContext ruleContext;
private final ImmutableList.Builder<Artifact> additionalTools;
private RunfilesSupport runfilesSupport;
@@ -435,7 +444,13 @@
tools.build(),
splitCoveragePostProcessing,
lcovMergerFilesToRun,
- lcovMergerRunfilesSupplier);
+ lcovMergerRunfilesSupplier,
+ // Network allowlist only makes sense in workspaces which explicitly add it, use an
+ // empty one as a fallback.
+ MoreObjects.firstNonNull(
+ Allowlist.fetchPackageSpecificationProviderOrNull(
+ ruleContext, "$network_allowlist"),
+ EMPTY_PACKAGES_PROVIDER));
testOutputs.addAll(testRunnerAction.getSpawnOutputs());
testOutputs.addAll(testRunnerAction.getOutputs());
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/test/TestRunnerAction.java b/src/main/java/com/google/devtools/build/lib/analysis/test/TestRunnerAction.java
index 0b21880..b23d95a 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/test/TestRunnerAction.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/test/TestRunnerAction.java
@@ -48,6 +48,7 @@
import com.google.devtools.build.lib.actions.SpawnExecutedEvent;
import com.google.devtools.build.lib.actions.SpawnResult;
import com.google.devtools.build.lib.actions.TestExecException;
+import com.google.devtools.build.lib.analysis.PackageSpecificationProvider;
import com.google.devtools.build.lib.analysis.SingleRunfilesSupplier;
import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
import com.google.devtools.build.lib.analysis.config.RunUnder;
@@ -157,6 +158,9 @@
private final NestedSetBuilder<Artifact> lcovMergerFilesToRun;
private final RunfilesSupplier lcovMergerRunfilesSupplier;
+ // TODO(b/192694287): Remove once we migrate all tests from the allowlist.
+ private final PackageSpecificationProvider networkAllowlist;
+
private static ImmutableSet<Artifact> nonNullAsSet(Artifact... artifacts) {
ImmutableSet.Builder<Artifact> builder = ImmutableSet.builder();
for (Artifact artifact : artifacts) {
@@ -198,7 +202,8 @@
Iterable<Artifact> tools,
boolean splitCoveragePostProcessing,
NestedSetBuilder<Artifact> lcovMergerFilesToRun,
- RunfilesSupplier lcovMergerRunfilesSupplier) {
+ RunfilesSupplier lcovMergerRunfilesSupplier,
+ PackageSpecificationProvider networkAllowlist) {
super(
owner,
NestedSetBuilder.wrap(Order.STABLE_ORDER, tools),
@@ -256,6 +261,7 @@
this.splitCoveragePostProcessing = splitCoveragePostProcessing;
this.lcovMergerFilesToRun = lcovMergerFilesToRun;
this.lcovMergerRunfilesSupplier = lcovMergerRunfilesSupplier;
+ this.networkAllowlist = networkAllowlist;
// Mark all possible test outputs for deletion before test execution.
// TestRunnerAction potentially can create many more non-declared outputs - xml output, coverage
@@ -824,6 +830,10 @@
return testLog;
}
+ public PackageSpecificationProvider getNetworkAllowlist() {
+ return networkAllowlist;
+ }
+
@Override
public ActionContinuationOrResult beginExecution(ActionExecutionContext actionExecutionContext)
throws InterruptedException, ActionExecutionException {