Unify the options initialization and error handling in BuildEventServiceModule.
The error checking for the obtaining the options classes from the command environment
were scattered all over the class. Use a single method to initialize them and reset
them with every command.
Unifying these options uncovered a brittle dependency between BuildEventServiceModule and BazelBuildEventServiceModuleTest since it was relying on the streamer creation directly. The test now exercises the logic from BuildEventServiceModule.
PiperOrigin-RevId: 234489667
diff --git a/src/main/java/com/google/devtools/build/lib/buildeventservice/BuildEventServiceModule.java b/src/main/java/com/google/devtools/build/lib/buildeventservice/BuildEventServiceModule.java
index b880da6..5d347c7 100644
--- a/src/main/java/com/google/devtools/build/lib/buildeventservice/BuildEventServiceModule.java
+++ b/src/main/java/com/google/devtools/build/lib/buildeventservice/BuildEventServiceModule.java
@@ -11,18 +11,14 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
-
package com.google.devtools.build.lib.buildeventservice;
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.base.Strings.isNullOrEmpty;
-import static java.lang.String.format;
-
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
+import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.devtools.build.lib.authandtls.AuthAndTLSOptions;
@@ -59,14 +55,18 @@
* Module responsible for the Build Event Transport (BEP) and Build Event Service (BES)
* functionality.
*/
-public abstract class BuildEventServiceModule<T extends BuildEventServiceOptions>
+public abstract class BuildEventServiceModule<BESOptionsT extends BuildEventServiceOptions>
extends BlazeModule {
private static final Logger logger = Logger.getLogger(BuildEventServiceModule.class.getName());
private OutErr outErr;
private BuildEventStreamer streamer;
- private boolean keepClient;
+ private BESOptionsT besOptions;
+ private BuildEventProtocolOptions bepOptions;
+ private AuthAndTLSOptions authTlsOptions;
+ private BuildEventStreamOptions besStreamOptions;
+ private ImmutableSet<BuildEventTransport> bepTransports;
/** Whether an error in the Build Event Service upload causes the build to fail. */
protected boolean errorsShouldFailTheBuild() {
@@ -77,9 +77,16 @@
protected void reportError(
EventHandler commandLineReporter,
ModuleEnvironment moduleEnvironment,
- AbruptExitException exception) {
+ String msg,
+ Exception exception,
+ ExitCode exitCode) {
+ // Don't hide unchecked exceptions as part of the error reporting.
+ Throwables.throwIfUnchecked(exception);
+
+ logger.log(Level.SEVERE, msg, exception);
+ AbruptExitException abruptException = new AbruptExitException(msg, exitCode, exception);
commandLineReporter.handle(Event.error(exception.getMessage()));
- moduleEnvironment.exit(exception);
+ moduleEnvironment.exit(abruptException);
}
@Override
@@ -93,7 +100,13 @@
@Override
public void beforeCommand(CommandEnvironment commandEnvironment) {
- T besOptions = commandEnvironment.getOptions().getOptions(optionsClass());
+ OptionsParsingResult parsingResult = commandEnvironment.getOptions();
+ besOptions = Preconditions.checkNotNull(parsingResult.getOptions(optionsClass()));
+ bepOptions =
+ Preconditions.checkNotNull(parsingResult.getOptions(BuildEventProtocolOptions.class));
+ authTlsOptions = Preconditions.checkNotNull(parsingResult.getOptions(AuthAndTLSOptions.class));
+ besStreamOptions =
+ Preconditions.checkNotNull(parsingResult.getOptions(BuildEventStreamOptions.class));
// Reset to null in case afterCommand was not called.
this.outErr = null;
@@ -125,7 +138,6 @@
});
err.registerStreamer(streamer);
out.registerStreamer(streamer);
- logger.fine("BuildEventStreamer created and registered successfully.");
}
}
@@ -156,30 +168,23 @@
this.streamer = null;
}
- if (!keepClient) {
+ if (!besStreamOptions.keepBackendConnections) {
clearBesClient();
}
this.outErr = null;
+ this.bepTransports = null;
}
/** Returns {@code null} if no stream could be created. */
@Nullable
@VisibleForTesting
BuildEventStreamer tryCreateStreamer(CommandEnvironment env) {
- BuildEventStreamOptions buildEventStreamOptions =
- env.getOptions().getOptions(BuildEventStreamOptions.class);
- this.keepClient = buildEventStreamOptions.keepBackendConnections;
-
- BuildEventProtocolOptions protocolOptions =
- checkNotNull(
- env.getOptions().getOptions(BuildEventProtocolOptions.class),
- "Could not get BuildEventProtocolOptions.");
Supplier<BuildEventArtifactUploader> uploaderSupplier =
Suppliers.memoize(
() ->
env.getRuntime()
.getBuildEventArtifactUploaderFactoryMap()
- .select(protocolOptions.buildEventUploadStrategy)
+ .select(bepOptions.buildEventUploadStrategy)
.create(env));
CountingArtifactGroupNamer namer = new CountingArtifactGroupNamer();
@@ -188,40 +193,41 @@
try {
besTransport = tryCreateBesTransport(env, uploaderSupplier, namer);
} catch (IOException | OptionsParsingException e) {
- String message = "Failed to create BuildEventTransport: " + e;
- logger.log(Level.WARNING, message, e);
- ExitCode exitCode =
- (e instanceof OptionsParsingException)
- ? ExitCode.COMMAND_LINE_ERROR
- : ExitCode.TRANSIENT_BUILD_EVENT_SERVICE_UPLOAD_ERROR;
reportError(
env.getReporter(),
env.getBlazeModuleEnvironment(),
- new AbruptExitException(message, exitCode, e));
+ "Failed to create BuildEventTransport: " + e,
+ e,
+ (e instanceof OptionsParsingException)
+ ? ExitCode.COMMAND_LINE_ERROR
+ : ExitCode.TRANSIENT_BUILD_EVENT_SERVICE_UPLOAD_ERROR);
clearBesClient();
return null;
}
- ImmutableSet<BuildEventTransport> bepTransports =
+ ImmutableSet<BuildEventTransport> bepFileTransports =
BuildEventTransportFactory.createFromOptions(
- env, env.getBlazeModuleEnvironment()::exit, protocolOptions, uploaderSupplier, namer);
+ env, env.getBlazeModuleEnvironment()::exit, bepOptions, uploaderSupplier, namer);
ImmutableSet.Builder<BuildEventTransport> transportsBuilder =
- ImmutableSet.<BuildEventTransport>builder().addAll(bepTransports);
+ ImmutableSet.<BuildEventTransport>builder().addAll(bepFileTransports);
if (besTransport != null) {
transportsBuilder.add(besTransport);
}
- ImmutableSet<BuildEventTransport> transports = transportsBuilder.build();
- if (!transports.isEmpty()) {
- return new BuildEventStreamer(
- transports, env.getReporter(), buildEventStreamOptions, namer);
+ bepTransports = transportsBuilder.build();
+ if (!bepTransports.isEmpty()) {
+ return new BuildEventStreamer(bepTransports, env.getReporter(), besStreamOptions, namer);
}
} catch (Exception e) {
+ // TODO(lpino): This generic catch with Exception shouldn't exist, remove it once the code
+ // above is re-structured.
reportError(
env.getReporter(),
env.getBlazeModuleEnvironment(),
- new AbruptExitException(ExitCode.LOCAL_ENVIRONMENTAL_ERROR, e));
+ e.getMessage(),
+ e,
+ ExitCode.LOCAL_ENVIRONMENTAL_ERROR);
}
return null;
}
@@ -233,27 +239,11 @@
ArtifactGroupNamer namer)
throws IOException, OptionsParsingException {
OptionsParsingResult optionsProvider = env.getOptions();
- T besOptions =
- checkNotNull(
- optionsProvider.getOptions(optionsClass()), "Could not get BuildEventServiceOptions.");
- AuthAndTLSOptions authTlsOptions =
- checkNotNull(
- optionsProvider.getOptions(AuthAndTLSOptions.class),
- "Could not get AuthAndTLSOptions.");
- BuildEventProtocolOptions protocolOptions =
- checkNotNull(
- optionsProvider.getOptions(BuildEventProtocolOptions.class),
- "Could not get BuildEventProtocolOptions.");
- if (isNullOrEmpty(besOptions.besBackend)) {
- logger.fine("BuildEventServiceTransport is disabled.");
+ if (Strings.isNullOrEmpty(besOptions.besBackend)) {
clearBesClient();
return null;
} else {
- logger.fine(
- format(
- "Will create BuildEventServiceTransport streaming to '%s'", besOptions.besBackend));
-
String invocationId = env.getCommandId().toString();
final String besResultsUrl;
if (!Strings.isNullOrEmpty(besOptions.besResultsUrl)) {
@@ -267,7 +257,7 @@
env.getReporter()
.handle(
Event.info(
- format(
+ String.format(
"Streaming Build Event Protocol to %s build_request_id: %s "
+ "invocation_id: %s",
besOptions.besBackend, env.getBuildRequestId(), invocationId)));
@@ -292,29 +282,28 @@
.build(
client,
artifactUploader,
- protocolOptions,
+ bepOptions,
besProtoUtil,
env.getRuntime().getClock(),
bazelExitFunction(
env.getReporter(), env.getBlazeModuleEnvironment(), besResultsUrl),
namer);
- logger.fine("BuildEventServiceTransport was created successfully");
return besTransport;
}
}
- protected abstract Class<T> optionsClass();
+ protected abstract Class<BESOptionsT> optionsClass();
protected abstract BuildEventServiceClient getBesClient(
- T besOptions, AuthAndTLSOptions authAndTLSOptions)
+ BESOptionsT besOptions, AuthAndTLSOptions authAndTLSOptions)
throws IOException, OptionsParsingException;
protected abstract void clearBesClient();
- protected abstract Set<String> whitelistedCommands(T besOptions);
+ protected abstract Set<String> whitelistedCommands(BESOptionsT besOptions);
protected Set<String> keywords(
- T besOptions, @Nullable OptionsParsingResult startupOptionsProvider) {
+ BESOptionsT besOptions, @Nullable OptionsParsingResult startupOptionsProvider) {
return besOptions.besKeywords.stream()
.map(keyword -> "user_keyword=" + keyword)
.collect(ImmutableSet.toImmutableSet());
@@ -348,4 +337,11 @@
}
};
}
+
+ // TODO(lpino): This method shouldn exist. It only does because some tests are relying on the
+ // transport creation logic of this module directly.
+ @VisibleForTesting
+ ImmutableSet<BuildEventTransport> getBepTransports() {
+ return bepTransports;
+ }
}