Coverage for /builds/BuildGrid/buildgrid/buildgrid/server/introspection/instance.py: 96.08%
51 statements
« prev ^ index » next coverage.py v7.4.1, created at 2025-05-28 16:48 +0000
« prev ^ index » next coverage.py v7.4.1, created at 2025-05-28 16:48 +0000
1# Copyright (C) 2024 Bloomberg LP
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# <http://www.apache.org/licenses/LICENSE-2.0>
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
15from google.protobuf.timestamp_pb2 import Timestamp
17from buildgrid._protos.buildgrid.v2.introspection_pb2 import (
18 DESCRIPTOR,
19 ListWorkersRequest,
20 ListWorkersResponse,
21 OperationFilter,
22 OperationFilters,
23 RegisteredWorker,
24 WorkerOperation,
25)
26from buildgrid.server.enums import BotStatus
27from buildgrid.server.exceptions import InvalidArgumentError
28from buildgrid.server.operations.filtering.interpreter import VALID_OPERATION_FILTERS, OperationFilterSpec
29from buildgrid.server.operations.filtering.sanitizer import DatetimeValueSanitizer, SortKeyValueSanitizer
30from buildgrid.server.scheduler.impl import Scheduler
31from buildgrid.server.servicer import Instance
32from buildgrid.server.settings import MAX_LIST_PAGE_SIZE
33from buildgrid.server.utils.digests import parse_digest
36class IntrospectionInstance(Instance):
37 SERVICE_NAME = DESCRIPTOR.services_by_name["Introspection"].full_name
39 def __init__(self, sql: Scheduler) -> None:
40 self._scheduler = sql
42 def list_workers(self, request: ListWorkersRequest) -> ListWorkersResponse:
43 if request.page_size < 0:
44 raise InvalidArgumentError("Page size must be a positive integer")
45 if request.page < 0:
46 raise InvalidArgumentError("Page number must be a positive integer")
48 page = request.page or 1
49 page_size = min(request.page_size, MAX_LIST_PAGE_SIZE) or MAX_LIST_PAGE_SIZE
50 bot_entries, count = self._scheduler.list_workers(request.worker_name, page, page_size)
52 workers = []
53 for bot in bot_entries:
54 last_update = Timestamp()
55 last_update.FromDatetime(bot.last_update_timestamp)
56 expiry: Timestamp | None = None
57 if bot.expiry_time is not None:
58 expiry = Timestamp()
59 expiry.FromDatetime(bot.expiry_time)
60 worker = RegisteredWorker(
61 worker_name=bot.bot_id,
62 session_name=bot.name,
63 last_updated=last_update,
64 bot_status=BotStatus(bot.bot_status).value,
65 expiry_time=expiry,
66 )
67 if bot.job is not None:
68 if digest := parse_digest(bot.job.action_digest):
69 worker.action_digest.CopyFrom(digest)
70 worker.operations.extend(
71 [WorkerOperation(operation_name=operation.name) for operation in bot.job.operations]
72 )
73 workers.append(worker)
75 return ListWorkersResponse(workers=workers, total=count, page=page, page_size=page_size)
77 def get_operation_filters(self) -> OperationFilters:
78 def _generate_filter_spec(key: str, spec: OperationFilterSpec) -> OperationFilter:
79 comparators = ["<", "<=", "=", "!=", ">=", ">"]
80 filter_type = "text"
81 if isinstance(spec.sanitizer, SortKeyValueSanitizer):
82 comparators = ["="]
83 elif isinstance(spec.sanitizer, DatetimeValueSanitizer):
84 filter_type = "datetime"
86 try:
87 values = spec.sanitizer.valid_values
88 except NotImplementedError:
89 values = []
91 return OperationFilter(
92 key=key,
93 name=spec.name,
94 type=filter_type,
95 description=spec.description,
96 comparators=comparators,
97 values=values,
98 )
100 return OperationFilters(
101 filters=[_generate_filter_spec(key, spec) for key, spec in VALID_OPERATION_FILTERS.items()]
102 )