Coverage for /builds/BuildGrid/buildgrid/buildgrid/server/operations/instance.py: 98.31%

59 statements  

« prev     ^ index     » next       coverage.py v7.4.1, created at 2024-06-11 15:37 +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. 

14 

15 

16""" 

17OperationsInstance 

18================== 

19An instance of the LongRunningOperations Service. 

20""" 

21import logging 

22from typing import Dict, Optional, Tuple 

23 

24from buildgrid._exceptions import InvalidArgumentError, NotFoundError, RetriableError 

25from buildgrid._protos.google.longrunning.operations_pb2 import DESCRIPTOR as OPS_DESCRIPTOR 

26from buildgrid._protos.google.longrunning.operations_pb2 import ListOperationsResponse, Operation 

27from buildgrid.server.metrics_names import ( 

28 OPERATIONS_CANCEL_OPERATION_EXCEPTION_COUNT_METRIC_NAME, 

29 OPERATIONS_CANCEL_OPERATION_TIME_METRIC_NAME, 

30 OPERATIONS_GET_OPERATION_EXCEPTION_COUNT_METRIC_NAME, 

31 OPERATIONS_GET_OPERATION_TIME_METRIC_NAME, 

32 OPERATIONS_LIST_OPERATIONS_EXCEPTION_COUNT_METRIC_NAME, 

33 OPERATIONS_LIST_OPERATIONS_TIME_METRIC_NAME, 

34) 

35from buildgrid.server.metrics_utils import DurationMetric, ExceptionCounter 

36from buildgrid.server.operations.filtering import DEFAULT_OPERATION_FILTERS, FilterParser 

37from buildgrid.server.persistence.sql.impl import SQLDataStore 

38from buildgrid.server.servicer import Instance 

39from buildgrid.settings import DEFAULT_MAX_LIST_OPERATION_PAGE_SIZE 

40 

41LOGGER = logging.getLogger(__name__) 

42 

43 

44class OperationsInstance(Instance): 

45 SERVICE_NAME = OPS_DESCRIPTOR.services_by_name["Operations"].full_name 

46 

47 def __init__( 

48 self, scheduler: SQLDataStore, max_list_operations_page_size: int = DEFAULT_MAX_LIST_OPERATION_PAGE_SIZE 

49 ) -> None: 

50 self.scheduler = scheduler 

51 self._max_list_operations_page_size = max_list_operations_page_size 

52 

53 # --- Public API --- 

54 

55 def start(self) -> None: 

56 self.scheduler.start() 

57 

58 def stop(self) -> None: 

59 self.scheduler.stop() 

60 LOGGER.info(f"Stopped Operations instance for '{self._instance_name}'") 

61 

62 get_operation_ignored_exceptions = (RetriableError,) 

63 

64 @DurationMetric(OPERATIONS_GET_OPERATION_TIME_METRIC_NAME, instanced=True) 

65 @ExceptionCounter( 

66 OPERATIONS_GET_OPERATION_EXCEPTION_COUNT_METRIC_NAME, 

67 ignored_exceptions=get_operation_ignored_exceptions, 

68 ) 

69 def get_operation(self, operation_name: str) -> Tuple[Operation, Optional[Dict[str, str]]]: 

70 try: 

71 operation = self.scheduler.load_operation(operation_name) 

72 

73 except NotFoundError: 

74 raise InvalidArgumentError(f"Operation name does not exist: [{operation_name}]") 

75 

76 metadata = self.scheduler.get_operation_request_metadata_by_name(operation_name) 

77 return operation, metadata 

78 

79 list_operations_ignored_exceptions = (RetriableError,) 

80 

81 @DurationMetric(OPERATIONS_LIST_OPERATIONS_TIME_METRIC_NAME, instanced=True) 

82 @ExceptionCounter( 

83 OPERATIONS_LIST_OPERATIONS_EXCEPTION_COUNT_METRIC_NAME, 

84 ignored_exceptions=list_operations_ignored_exceptions, 

85 ) 

86 def list_operations( 

87 self, filter_string: str, page_size: Optional[int], page_token: Optional[str] 

88 ) -> ListOperationsResponse: 

89 if page_size and page_size > self._max_list_operations_page_size: 

90 raise InvalidArgumentError(f"The maximum page size is {self._max_list_operations_page_size}.") 

91 if not page_size: 

92 page_size = self._max_list_operations_page_size 

93 

94 operation_filters = FilterParser.parse_listoperations_filters(filter_string) 

95 if not operation_filters: 

96 operation_filters = DEFAULT_OPERATION_FILTERS 

97 

98 response = ListOperationsResponse() 

99 

100 results, next_token = self.scheduler.list_operations(operation_filters, page_size, page_token) 

101 response.operations.extend(results) 

102 response.next_page_token = next_token 

103 

104 return response 

105 

106 def delete_operation(self, job_name: str) -> None: 

107 """DeleteOperation is not supported in BuildGrid.""" 

108 pass 

109 

110 cancel_operation_ignored_exceptions = (NotFoundError, RetriableError) 

111 

112 @DurationMetric(OPERATIONS_CANCEL_OPERATION_TIME_METRIC_NAME, instanced=True) 

113 @ExceptionCounter( 

114 OPERATIONS_CANCEL_OPERATION_EXCEPTION_COUNT_METRIC_NAME, 

115 ignored_exceptions=cancel_operation_ignored_exceptions, 

116 ) 

117 def cancel_operation(self, operation_name: str) -> None: 

118 try: 

119 self.scheduler.cancel_operation(operation_name) 

120 

121 except NotFoundError: 

122 raise InvalidArgumentError(f"Operation name does not exist: [{operation_name}]")