processors/loadbalancing.py (134 lines of code) (raw):
# Copyright 2023 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from .base import Processor, NotConfiguredException
import google.auth
from googleapiclient import discovery
import time
class LoadbalancingOperationFailed(Exception):
pass
class LoadbalancingProcessor(Processor):
"""
Perform actions on Cloud Load Balancers.
Args:
project (str, optional): Google Cloud project ID.
backend_service (str): Backend service to operate on.
timeout (int, optional): Timeout of waiting on operations.
region (str, optional): Google Cloud region.
mode (str): One of: backendservice.get, regionbackendservice.get, backendservice.patch, regionbackendservice.patch
"""
def get_default_config_key():
return 'loadbalancing'
def wait_for_operation_done(self,
compute_service,
operation_name,
operation_self_link,
project,
zone,
region,
timeout=30):
end_time = start_time = time.monotonic()
while True and (end_time - start_time) < timeout:
if '/global/' in operation_self_link:
op_request = compute_service.globalOperations().get(
project=project, operation=operation_name).execute()
elif '/zones/' in operation_self_link:
op_request = compute_service.zoneOperations().get(
project=project, zone=zone,
operation=operation_name).execute()
else:
op_request = compute_service.regionOperations().get(
project=project, region=region,
operation=operation_name).execute()
if 'status' in op_request and op_request['status'] == 'DONE':
if 'error' in op_request:
self.logger.error(
'Error while waiting for long running operation %s to complete.'
% (operation_name),
extra={'error': op_request['error']})
return op_request['error']
return op_request
time.sleep(5)
end_time = time.monotonic()
def get_backend(self, compute_service, project, backend_service):
get_request = compute_service.backendServices().get(
project=project, backendService=backend_service)
get_response = get_request.execute()
if 'id' in get_response:
return get_response
else:
raise LoadbalancingOperationFailed(
'Failed to get backend service: %s' % str(get_response))
def get_region_backend(self, compute_service, project, region,
backend_service):
get_request = compute_service.regionBackendServices().get(
project=project, region=region, backendService=backend_service)
get_response = get_request.execute()
if 'id' in get_response:
return get_response
else:
raise LoadbalancingOperationFailed(
'Failed to get regional backend service: %s' %
str(get_response))
def process(self, output_var='loadbalancing'):
if 'mode' not in self.config:
raise NotConfiguredException(
'No Cloud Load Balancing operation specified.')
if 'backendService' not in self.config:
raise NotConfiguredException(
'No backend service name specified in the configuration.')
credentials, credentials_project_id = google.auth.default()
project = self._jinja_expand_string(
self.config['project'],
'project') if 'project' in self.config else credentials_project_id
if not project:
project = credentials.quota_project_id
compute_service = discovery.build(
'compute', 'v1', http=self._get_branded_http(credentials))
timeout = self._jinja_expand_int(
self.config['timeout']) if 'timeout' in self.config else 30
backend_service = self._jinja_expand_string(
self.config['backendService'], 'backend_service')
if self.config['mode'] == 'backendservice.get':
return {
output_var:
self.get_backend(compute_service, project, backend_service)
}
if self.config['mode'] == 'regionbackendservice.get':
region = self._jinja_expand_string(self.config['region'], 'region')
return {
output_var:
self.get_region_backend(compute_service, project, region,
backend_service)
}
if 'patch' not in self.config:
raise NotConfiguredException(
'No backend service patch fields specified in the configuration.'
)
patches = self._jinja_expand_dict_all(self.config['patch'], 'patch')
if self.config['mode'] == 'backendservice.patch':
backend = self.get_backend(compute_service, project,
backend_service)
patch_request = compute_service.backendServices().patch(
project=project, backendService=backend_service, body=patches)
patch_response = patch_request.execute()
if 'id' in patch_response:
self.wait_for_operation_done(compute_service,
patch_response['id'],
patch_response['selfLink'],
project, None, region, timeout)
backend = self.get_backend(compute_service, project,
backend_service)
return {output_var: backend}
if self.config['mode'] == 'regionbackendservice.patch':
if 'region' not in self.config:
raise NotConfiguredException(
'No backend service region specified in the configuration.')
region = self._jinja_expand_string(self.config['region'], 'region')
backend = self.get_region_backend(compute_service, project, region,
backend_service)
patch_request = compute_service.regionBackendServices().patch(
project=project,
region=region,
backendService=backend_service,
body=patches)
patch_response = patch_request.execute()
if 'id' in patch_response:
self.wait_for_operation_done(compute_service,
patch_response['id'],
patch_response['selfLink'],
project, None, region, timeout)
backend = self.get_region_backend(compute_service, project,
region, backend_service)
return {output_var: backend}
return {
output_var: None,
}