// Copyright 2017 The Bazel Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // 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.devtools.build.v1.BuildEvent.BuildComponentStreamFinished.FinishType.FINISHED; import com.google.common.annotations.VisibleForTesting; import com.google.devtools.build.lib.util.Clock; import com.google.devtools.build.v1.BuildEvent; import com.google.devtools.build.v1.BuildEvent.BuildComponentStreamFinished; import com.google.devtools.build.v1.BuildEvent.BuildEnqueued; import com.google.devtools.build.v1.BuildEvent.BuildFinished; import com.google.devtools.build.v1.BuildEvent.EventCase; import com.google.devtools.build.v1.BuildEvent.InvocationAttemptFinished; import com.google.devtools.build.v1.BuildEvent.InvocationAttemptStarted; import com.google.devtools.build.v1.BuildStatus; import com.google.devtools.build.v1.BuildStatus.Result; import com.google.devtools.build.v1.OrderedBuildEvent; import com.google.devtools.build.v1.PublishLifecycleEventRequest; import com.google.devtools.build.v1.StreamId; import com.google.devtools.build.v1.StreamId.BuildComponent; import com.google.protobuf.Any; import com.google.protobuf.util.Timestamps; import java.util.concurrent.atomic.AtomicInteger; import javax.annotation.Nullable; /** Utility class used to build protobuffs requests that are meant to be sent over BES. */ public final class BuildEventServiceProtoUtil { private final String buildRequestId; private final String buildInvocationId; private final String projectId; private final AtomicInteger streamSequenceNumber; private final Clock clock; public BuildEventServiceProtoUtil(String buildRequestId, String buildInvocationId, @Nullable String projectId, Clock clock) { this.buildRequestId = buildRequestId; this.buildInvocationId = buildInvocationId; this.projectId = projectId; this.clock = clock; this.streamSequenceNumber = new AtomicInteger(1); } public PublishLifecycleEventRequest buildEnqueued() { return lifecycleEvent(projectId, 1, com.google.devtools.build.v1.BuildEvent.newBuilder() .setEventTime(Timestamps.fromMillis(clock.currentTimeMillis())) .setBuildEnqueued(BuildEnqueued.newBuilder())) .build(); } public PublishLifecycleEventRequest buildFinished(Result result) { return lifecycleEvent(projectId, 2, com.google.devtools.build.v1.BuildEvent.newBuilder() .setEventTime(Timestamps.fromMillis(clock.currentTimeMillis())) .setBuildFinished( BuildFinished.newBuilder() .setStatus(BuildStatus.newBuilder().setResult(result)))) .build(); } public PublishLifecycleEventRequest invocationStarted() { return lifecycleEvent(projectId, 1, com.google.devtools.build.v1.BuildEvent.newBuilder() .setEventTime(Timestamps.fromMillis(clock.currentTimeMillis())) .setInvocationAttemptStarted( InvocationAttemptStarted.newBuilder().setAttemptNumber(1))) .build(); } public PublishLifecycleEventRequest invocationFinished(Result result) { return lifecycleEvent(projectId, 2, com.google.devtools.build.v1.BuildEvent.newBuilder() .setEventTime(Timestamps.fromMillis(clock.currentTimeMillis())) .setInvocationAttemptFinished( InvocationAttemptFinished.newBuilder() .setInvocationStatus(BuildStatus.newBuilder().setResult(result)))) .build(); } /** Utility method used to create a OrderedBuildEvent that delimits the end of the stream. */ public OrderedBuildEvent streamFinished() { return streamFinished(streamSequenceNumber.getAndIncrement()); } /** Utility method used to create a OrderedBuildEvent from an packed bazel event */ public OrderedBuildEvent bazelEvent(Any packedEvent) { return bazelEvent(streamSequenceNumber.getAndIncrement(), packedEvent); } @VisibleForTesting public OrderedBuildEvent bazelEvent(int sequenceNumber, Any packedEvent) { return orderedBuildEvent( sequenceNumber, com.google.devtools.build.v1.BuildEvent.newBuilder().setBazelEvent(packedEvent)); } @VisibleForTesting public OrderedBuildEvent streamFinished(int sequenceNumber) { return orderedBuildEvent( sequenceNumber, BuildEvent.newBuilder() .setComponentStreamFinished( BuildComponentStreamFinished.newBuilder().setType(FINISHED))); } @VisibleForTesting public OrderedBuildEvent orderedBuildEvent(int sequenceNumber, BuildEvent.Builder besEvent) { return OrderedBuildEvent.newBuilder() .setSequenceNumber(sequenceNumber) .setEvent(besEvent.setEventTime(Timestamps.fromMillis(clock.currentTimeMillis()))) .setStreamId(streamId(besEvent.getEventCase())) .build(); } @VisibleForTesting public PublishLifecycleEventRequest.Builder lifecycleEvent(@Nullable String projectId, int sequenceNumber, BuildEvent.Builder lifecycleEvent) { PublishLifecycleEventRequest.Builder builder = PublishLifecycleEventRequest.newBuilder() .setServiceLevel(PublishLifecycleEventRequest.ServiceLevel.INTERACTIVE) .setBuildEvent( OrderedBuildEvent.newBuilder() .setSequenceNumber(sequenceNumber) .setStreamId(streamId(lifecycleEvent.getEventCase())) .setEvent(lifecycleEvent)); if (projectId != null) { builder.setProjectId(projectId); } return builder; } @VisibleForTesting public StreamId streamId(EventCase eventCase) { StreamId.Builder streamId = StreamId.newBuilder().setBuildId(buildRequestId); switch (eventCase) { case BUILD_ENQUEUED: case BUILD_FINISHED: streamId.setComponent(BuildComponent.CONTROLLER); break; case INVOCATION_ATTEMPT_STARTED: case INVOCATION_ATTEMPT_FINISHED: streamId.setInvocationId(buildInvocationId); streamId.setComponent(BuildComponent.CONTROLLER); break; case BAZEL_EVENT: case COMPONENT_STREAM_FINISHED: streamId.setInvocationId(buildInvocationId); streamId.setComponent(BuildComponent.TOOL); break; default: throw new IllegalArgumentException("Illegal EventCase " + eventCase); } return streamId.build(); } }