Coverage for /builds/BuildGrid/buildgrid/buildgrid/server/introspection/instance.py: 96.15%
52 statements
« prev ^ index » next coverage.py v7.4.1, created at 2024-10-04 17:48 +0000
« prev ^ index » next coverage.py v7.4.1, created at 2024-10-04 17: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 typing import Optional
17from google.protobuf.timestamp_pb2 import Timestamp
19from buildgrid._protos.buildgrid.v2.introspection_pb2 import (
20 DESCRIPTOR,
21 ListWorkersRequest,
22 ListWorkersResponse,
23 OperationFilter,
24 OperationFilters,
25 RegisteredWorker,
26 WorkerOperation,
27)
28from buildgrid.server.enums import BotStatus
29from buildgrid.server.exceptions import InvalidArgumentError
30from buildgrid.server.operations.filtering.interpreter import VALID_OPERATION_FILTERS, OperationFilterSpec
31from buildgrid.server.operations.filtering.sanitizer import DatetimeValueSanitizer, SortKeyValueSanitizer
32from buildgrid.server.scheduler.impl import Scheduler
33from buildgrid.server.servicer import Instance
34from buildgrid.server.settings import MAX_LIST_PAGE_SIZE
35from buildgrid.server.utils.digests import parse_digest
38class IntrospectionInstance(Instance):
39 SERVICE_NAME = DESCRIPTOR.services_by_name["Introspection"].full_name
41 def __init__(self, sql: Scheduler) -> None:
42 self._scheduler = sql
44 def list_workers(self, request: ListWorkersRequest) -> ListWorkersResponse:
45 if request.page_size < 0:
46 raise InvalidArgumentError("Page size must be a positive integer")
47 if request.page < 0:
48 raise InvalidArgumentError("Page number must be a positive integer")
50 page = request.page or 1
51 page_size = min(request.page_size, MAX_LIST_PAGE_SIZE) or MAX_LIST_PAGE_SIZE
52 bot_entries, count = self._scheduler.list_workers(request.worker_name, page, page_size)
54 workers = []
55 for bot in bot_entries:
56 last_update = Timestamp()
57 last_update.FromDatetime(bot.last_update_timestamp)
58 expiry: Optional[Timestamp] = None
59 if bot.expiry_time is not None:
60 expiry = Timestamp()
61 expiry.FromDatetime(bot.expiry_time)
62 worker = RegisteredWorker(
63 worker_name=bot.bot_id,
64 session_name=bot.name,
65 last_updated=last_update,
66 bot_status=BotStatus(bot.bot_status).value,
67 expiry_time=expiry,
68 )
69 if bot.job is not None:
70 if digest := parse_digest(bot.job.action_digest):
71 worker.action_digest.CopyFrom(digest)
72 worker.operations.extend(
73 [WorkerOperation(operation_name=operation.name) for operation in bot.job.operations]
74 )
75 workers.append(worker)
77 return ListWorkersResponse(workers=workers, total=count, page=page, page_size=page_size)
79 def get_operation_filters(self) -> OperationFilters:
80 def _generate_filter_spec(key: str, spec: OperationFilterSpec) -> OperationFilter:
81 comparators = ["<", "<=", "=", "!=", ">=", ">"]
82 filter_type = "text"
83 if isinstance(spec.sanitizer, SortKeyValueSanitizer):
84 comparators = ["="]
85 elif isinstance(spec.sanitizer, DatetimeValueSanitizer):
86 filter_type = "datetime"
88 try:
89 values = spec.sanitizer.valid_values
90 except NotImplementedError:
91 values = []
93 return OperationFilter(
94 key=key,
95 name=spec.name,
96 type=filter_type,
97 description=spec.description,
98 comparators=comparators,
99 values=values,
100 )
102 return OperationFilters(
103 filters=[_generate_filter_spec(key, spec) for key, spec in VALID_OPERATION_FILTERS.items()]
104 )