Coverage for /builds/BuildGrid/buildgrid/buildgrid/server/bots/service.py: 92.86%
42 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) 2018 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.
16"""
17BotsService
18=================
20"""
22from typing import Dict, cast
24import grpc
25from google.protobuf import empty_pb2
27from buildgrid._protos.google.devtools.remoteworkers.v1test2.bots_pb2 import DESCRIPTOR as BOTS_DESCRIPTOR
28from buildgrid._protos.google.devtools.remoteworkers.v1test2.bots_pb2 import (
29 BotSession,
30 CreateBotSessionRequest,
31 PostBotEventTempRequest,
32 UpdateBotSessionRequest,
33)
34from buildgrid._protos.google.devtools.remoteworkers.v1test2.bots_pb2_grpc import (
35 BotsServicer,
36 add_BotsServicer_to_server,
37)
38from buildgrid.server.bots.instance import BotsInterface
39from buildgrid.server.decorators import rpc
40from buildgrid.server.enums import BotStatus
41from buildgrid.server.exceptions import InvalidArgumentError
42from buildgrid.server.servicer import InstancedServicer
43from buildgrid.server.utils.cancellation import CancellationContext
46def _instance_name_from_bot_name(name: str) -> str:
47 names = name.split("/")
48 return "/".join(names[:-1])
51class BotsService(BotsServicer, InstancedServicer[BotsInterface]):
52 SERVICE_NAME = "Bots"
53 REGISTER_METHOD = add_BotsServicer_to_server
54 FULL_NAME = BOTS_DESCRIPTOR.services_by_name[SERVICE_NAME].full_name
56 @rpc(instance_getter=lambda r: cast(str, r.parent))
57 def CreateBotSession(self, request: CreateBotSessionRequest, context: grpc.ServicerContext) -> BotSession:
58 return self.current_instance.create_bot_session(
59 request.bot_session, CancellationContext(context), deadline=context.time_remaining()
60 )
62 @rpc(instance_getter=lambda r: _instance_name_from_bot_name(r.name))
63 def UpdateBotSession(self, request: UpdateBotSessionRequest, context: grpc.ServicerContext) -> BotSession:
64 if request.name != request.bot_session.name:
65 raise InvalidArgumentError(
66 "Name in UpdateBotSessionRequest does not match BotSession. "
67 f" UpdateBotSessionRequest.name=[{request.name}] BotSession.name=[{request.bot_session.name}]"
68 )
70 bot_session, metadata = self.current_instance.update_bot_session(
71 request.bot_session, CancellationContext(context), deadline=context.time_remaining()
72 )
73 context.set_trailing_metadata(metadata) # type: ignore[arg-type] # tricky covariance issue.
75 return bot_session
77 @rpc(instance_getter=lambda r: _instance_name_from_bot_name(r.name))
78 def PostBotEventTemp(self, request: PostBotEventTempRequest, context: grpc.ServicerContext) -> empty_pb2.Empty:
79 context.set_code(grpc.StatusCode.UNIMPLEMENTED)
80 return empty_pb2.Empty()
82 def query_connected_bots_for_instance(self, instance_name: str) -> int:
83 if instance := self.instances.get(instance_name):
84 return instance.scheduler.job_assigner.listener_count(instance_name)
85 return 0
87 def count_bots_by_status(self, instance_name: str) -> Dict[BotStatus, int]:
88 if instance := self.instances.get(instance_name):
89 return instance.count_bots_by_status()
90 return {}