Martin Blanchard pushed to branch mablanch/83-executed-action-metadata at BuildGrid / buildgrid
Commits:
-
2c03d631
by Martin Blanchard at 2018-10-23T09:12:00Z
-
ea43d860
by Martin Blanchard at 2018-10-23T09:12:02Z
-
d4ac1110
by Martin Blanchard at 2018-10-23T09:12:02Z
5 changed files:
- .pylintrc
- buildgrid/_app/bots/dummy.py
- buildgrid/_app/bots/host.py
- buildgrid/server/job.py
- tests/integration/operations_service.py
Changes:
... | ... | @@ -184,7 +184,8 @@ ignore-on-opaque-inference=yes |
184 | 184 |
# List of class names for which member attributes should not be checked (useful
|
185 | 185 |
# for classes with dynamically set attributes). This supports the use of
|
186 | 186 |
# qualified names.
|
187 |
-ignored-classes=google.protobuf.any_pb2.Any
|
|
187 |
+ignored-classes=google.protobuf.any_pb2.Any,
|
|
188 |
+ google.protobuf.timestamp_pb2.Timestamp
|
|
188 | 189 |
|
189 | 190 |
# List of module names for which member attributes should not be checked
|
190 | 191 |
# (useful for modules/projects where namespaces are manipulated during runtime
|
... | ... | @@ -24,10 +24,23 @@ def work_dummy(context, lease): |
24 | 24 |
"""
|
25 | 25 |
lease.result.Clear()
|
26 | 26 |
|
27 |
- time.sleep(random.randint(1, 5))
|
|
28 |
- |
|
29 | 27 |
action_result = remote_execution_pb2.ActionResult()
|
30 | 28 |
|
29 |
+ # Simulation input-downloading phase:
|
|
30 |
+ action_result.execution_metadata.input_fetch_start_timestamp.GetCurrentTime()
|
|
31 |
+ time.sleep(random.random())
|
|
32 |
+ action_result.execution_metadata.input_fetch_completed_timestamp.GetCurrentTime()
|
|
33 |
+ |
|
34 |
+ # Simulation execution phase:
|
|
35 |
+ action_result.execution_metadata.execution_start_timestamp.GetCurrentTime()
|
|
36 |
+ time.sleep(random.random())
|
|
37 |
+ action_result.execution_metadata.execution_completed_timestamp.GetCurrentTime()
|
|
38 |
+ |
|
39 |
+ # Simulation output-uploading phase:
|
|
40 |
+ action_result.execution_metadata.output_upload_start_timestamp.GetCurrentTime()
|
|
41 |
+ time.sleep(random.random())
|
|
42 |
+ action_result.execution_metadata.output_upload_completed_timestamp.GetCurrentTime()
|
|
43 |
+ |
|
31 | 44 |
lease.result.Pack(action_result)
|
32 | 45 |
|
33 | 46 |
return lease
|
... | ... | @@ -29,11 +29,14 @@ def work_host_tools(context, lease): |
29 | 29 |
logger = context.logger
|
30 | 30 |
|
31 | 31 |
action_digest = remote_execution_pb2.Digest()
|
32 |
+ action_result = remote_execution_pb2.ActionResult()
|
|
32 | 33 |
|
33 | 34 |
lease.payload.Unpack(action_digest)
|
34 | 35 |
lease.result.Clear()
|
35 | 36 |
|
36 | 37 |
with tempfile.TemporaryDirectory() as temp_directory:
|
38 |
+ action_result.execution_metadata.input_fetch_start_timestamp.GetCurrentTime()
|
|
39 |
+ |
|
37 | 40 |
with download(context.cas_channel, instance=instance_name) as downloader:
|
38 | 41 |
action = downloader.get_message(action_digest,
|
39 | 42 |
remote_execution_pb2.Action())
|
... | ... | @@ -45,6 +48,8 @@ def work_host_tools(context, lease): |
45 | 48 |
|
46 | 49 |
downloader.download_directory(action.input_root_digest, temp_directory)
|
47 | 50 |
|
51 |
+ action_result.execution_metadata.input_fetch_completed_timestamp.GetCurrentTime()
|
|
52 |
+ |
|
48 | 53 |
environment = os.environ.copy()
|
49 | 54 |
for variable in command.environment_variables:
|
50 | 55 |
if variable.name not in ['PATH', 'PWD']:
|
... | ... | @@ -70,6 +75,8 @@ def work_host_tools(context, lease): |
70 | 75 |
|
71 | 76 |
logger.debug(' '.join(command_line))
|
72 | 77 |
|
78 |
+ action_result.execution_metadata.execution_start_timestamp.GetCurrentTime()
|
|
79 |
+ |
|
73 | 80 |
process = subprocess.Popen(command_line,
|
74 | 81 |
cwd=working_directory,
|
75 | 82 |
env=environment,
|
... | ... | @@ -80,7 +87,8 @@ def work_host_tools(context, lease): |
80 | 87 |
stdout, stderr = process.communicate()
|
81 | 88 |
returncode = process.returncode
|
82 | 89 |
|
83 |
- action_result = remote_execution_pb2.ActionResult()
|
|
90 |
+ action_result.execution_metadata.execution_completed_timestamp.GetCurrentTime()
|
|
91 |
+ |
|
84 | 92 |
# TODO: Upload to CAS or output RAW
|
85 | 93 |
# For now, just pass raw
|
86 | 94 |
# https://gitlab.com/BuildGrid/buildgrid/issues/90
|
... | ... | @@ -92,6 +100,8 @@ def work_host_tools(context, lease): |
92 | 100 |
logger.debug("Command stdout: [{}]".format(stdout))
|
93 | 101 |
logger.debug("Command exit code: [{}]".format(returncode))
|
94 | 102 |
|
103 |
+ action_result.execution_metadata.output_upload_start_timestamp.GetCurrentTime()
|
|
104 |
+ |
|
95 | 105 |
with upload(context.cas_channel, instance=instance_name) as uploader:
|
96 | 106 |
output_files, output_directories = [], []
|
97 | 107 |
|
... | ... | @@ -121,6 +131,8 @@ def work_host_tools(context, lease): |
121 | 131 |
|
122 | 132 |
action_result.output_directories.extend(output_directories)
|
123 | 133 |
|
134 |
+ action_result.execution_metadata.output_upload_completed_timestamp.GetCurrentTime()
|
|
135 |
+ |
|
124 | 136 |
lease.result.Pack(action_result)
|
125 | 137 |
|
126 | 138 |
return lease
|
... | ... | @@ -17,6 +17,8 @@ import logging |
17 | 17 |
import uuid
|
18 | 18 |
from enum import Enum
|
19 | 19 |
|
20 |
+from google.protobuf import timestamp_pb2
|
|
21 |
+ |
|
20 | 22 |
from buildgrid._protos.build.bazel.remote.execution.v2 import remote_execution_pb2
|
21 | 23 |
from buildgrid._protos.google.devtools.remoteworkers.v1test2 import bots_pb2
|
22 | 24 |
from buildgrid._protos.google.longrunning import operations_pb2
|
... | ... | @@ -60,6 +62,9 @@ class Job: |
60 | 62 |
|
61 | 63 |
self.__execute_response = None
|
62 | 64 |
self.__operation_metadata = remote_execution_pb2.ExecuteOperationMetadata()
|
65 |
+ self.__queued_timestamp = timestamp_pb2.Timestamp()
|
|
66 |
+ self.__worker_start_timestamp = timestamp_pb2.Timestamp()
|
|
67 |
+ self.__worker_completed_timestamp = timestamp_pb2.Timestamp()
|
|
63 | 68 |
|
64 | 69 |
self.__operation_metadata.action_digest.CopyFrom(action_digest)
|
65 | 70 |
self.__operation_metadata.stage = OperationStage.UNKNOWN.value
|
... | ... | @@ -177,10 +182,18 @@ class Job: |
177 | 182 |
self._lease.state = state.value
|
178 | 183 |
|
179 | 184 |
if self._lease.state == LeaseState.PENDING.value:
|
185 |
+ self.__worker_start_timestamp.Clear()
|
|
186 |
+ self.__worker_completed_timestamp.Clear()
|
|
187 |
+ |
|
180 | 188 |
self._lease.status.Clear()
|
181 | 189 |
self._lease.result.Clear()
|
182 | 190 |
|
191 |
+ elif self._lease.state == LeaseState.ACTIVE.value:
|
|
192 |
+ self.__worker_start_timestamp.GetCurrentTime()
|
|
193 |
+ |
|
183 | 194 |
elif self._lease.state == LeaseState.COMPLETED.value:
|
195 |
+ self.__worker_completed_timestamp.GetCurrentTime()
|
|
196 |
+ |
|
184 | 197 |
action_result = remote_execution_pb2.ActionResult()
|
185 | 198 |
|
186 | 199 |
# TODO: Make a distinction between build and bot failures!
|
... | ... | @@ -191,6 +204,11 @@ class Job: |
191 | 204 |
assert result.Is(action_result.DESCRIPTOR)
|
192 | 205 |
result.Unpack(action_result)
|
193 | 206 |
|
207 |
+ action_metadata = action_result.execution_metadata
|
|
208 |
+ action_metadata.queued_timestamp.CopyFrom(self.__worker_start_timestamp)
|
|
209 |
+ action_metadata.worker_start_timestamp.CopyFrom(self.__worker_start_timestamp)
|
|
210 |
+ action_metadata.worker_completed_timestamp.CopyFrom(self.__worker_completed_timestamp)
|
|
211 |
+ |
|
194 | 212 |
self.__execute_response = remote_execution_pb2.ExecuteResponse()
|
195 | 213 |
self.__execute_response.result.CopyFrom(action_result)
|
196 | 214 |
self.__execute_response.cached_result = False
|
... | ... | @@ -208,6 +226,8 @@ class Job: |
208 | 226 |
self.__operation_metadata.stage = stage.value
|
209 | 227 |
|
210 | 228 |
if self.__operation_metadata.stage == OperationStage.QUEUED.value:
|
229 |
+ if self.__queued_timestamp.ByteSize() == 0:
|
|
230 |
+ self.__queued_timestamp.GetCurrentTime()
|
|
211 | 231 |
self._n_tries += 1
|
212 | 232 |
|
213 | 233 |
elif self.__operation_metadata.stage == OperationStage.COMPLETED.value:
|
... | ... | @@ -144,7 +144,8 @@ def test_list_operations_with_result(instance, controller, execute_request, cont |
144 | 144 |
|
145 | 145 |
execute_response = remote_execution_pb2.ExecuteResponse()
|
146 | 146 |
response.operations[0].response.Unpack(execute_response)
|
147 |
- assert execute_response.result == action_result
|
|
147 |
+ |
|
148 |
+ assert execute_response.result.output_files == action_result.output_files
|
|
148 | 149 |
|
149 | 150 |
|
150 | 151 |
def test_list_operations_empty(instance, context):
|