204 lines
6.6 KiB
Python
204 lines
6.6 KiB
Python
# -*- coding: utf-8 -*- #
|
|
# Copyright 2023 Google LLC. All Rights Reserved.
|
|
#
|
|
# 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.
|
|
"""Exceptions for cloud deploy libraries."""
|
|
|
|
|
|
from googlecloudsdk.calliope import exceptions as c_exceptions
|
|
from googlecloudsdk.core import exceptions
|
|
|
|
HTTP_ERROR_FORMAT = 'Status code: {status_code}. {status_message}.'
|
|
|
|
|
|
class ParserError(exceptions.Error):
|
|
"""Error parsing JSON into a dictionary."""
|
|
|
|
def __init__(self, path, msg):
|
|
"""Initialize a exceptions.ParserError.
|
|
|
|
Args:
|
|
path: str, build artifacts file path.
|
|
msg: str, error message.
|
|
"""
|
|
msg = 'parsing {path}: {msg}'.format(
|
|
path=path,
|
|
msg=msg,
|
|
)
|
|
super(ParserError, self).__init__(msg)
|
|
|
|
|
|
class ReleaseInactiveError(exceptions.Error):
|
|
"""Error when a release is not deployed to any target."""
|
|
|
|
def __init__(self):
|
|
super(ReleaseInactiveError, self).__init__(
|
|
'This release is not deployed to a target in the active delivery '
|
|
'pipeline. Include the --to-target parameter to indicate which target '
|
|
'to promote to.'
|
|
)
|
|
|
|
|
|
class AbandonedReleaseError(exceptions.Error):
|
|
"""Error when an activity happens on an abandoned release."""
|
|
|
|
def __init__(self, error_msg, release_name):
|
|
error_template = '{} Release {} is abandoned.'.format(
|
|
error_msg, release_name
|
|
)
|
|
super(AbandonedReleaseError, self).__init__(error_template)
|
|
|
|
|
|
class NoStagesError(exceptions.Error):
|
|
"""Error when a release doesn't contain any pipeline stages."""
|
|
|
|
def __init__(self, release_name):
|
|
super(NoStagesError, self).__init__(
|
|
'No pipeline stages in the release {}.'.format(release_name)
|
|
)
|
|
|
|
|
|
class InvalidReleaseNameError(exceptions.Error):
|
|
"""Error when a release has extra $ signs after expanding template terms."""
|
|
|
|
def __init__(self, release_name, error_indices):
|
|
error_msg = (
|
|
"Invalid character '$'"
|
|
" for release name '{}' at indices:"
|
|
' {}. Did you mean to use $DATE or $TIME?'
|
|
)
|
|
super(InvalidReleaseNameError, self).__init__(
|
|
error_msg.format(release_name, error_indices)
|
|
)
|
|
|
|
|
|
class CloudDeployConfigError(exceptions.Error):
|
|
"""Error raised for errors in the cloud deploy yaml config."""
|
|
|
|
@classmethod
|
|
def for_unnamed_manifest(cls, num, message):
|
|
return cls(f'Error parsing manifest #{num}: {message}')
|
|
|
|
@classmethod
|
|
def for_resource(cls, kind, name, message):
|
|
return cls(f'Error parsing {kind} "{name}": {message}')
|
|
|
|
@classmethod
|
|
def for_resource_field(cls, kind, name, field, message):
|
|
return cls(f'Error parsing {kind} "{name}" field "{field}": {message}')
|
|
|
|
|
|
class ManifestTransformException(exceptions.Error):
|
|
"""Error raised when a manifest transform fails due to a bug."""
|
|
|
|
|
|
class ListRolloutsError(exceptions.Error):
|
|
"""Error when it failed to list the rollouts that belongs to a release."""
|
|
|
|
def __init__(self, release_name):
|
|
super(ListRolloutsError, self).__init__(
|
|
'Failed to list rollouts for {}.'.format(release_name)
|
|
)
|
|
|
|
|
|
class RedeployRolloutError(exceptions.Error):
|
|
"""Error when a rollout can't be redeployed.
|
|
|
|
Redeploy can only be used for rollouts that are in a SUCCEEDED or FAILED
|
|
state.
|
|
"""
|
|
|
|
def __init__(self, target_name, rollout_name, rollout_state):
|
|
error_msg = (
|
|
"Unable to redeploy target {}. Rollout {} is in state {} that can't "
|
|
'be redeployed'.format(target_name, rollout_name, rollout_state)
|
|
)
|
|
super(RedeployRolloutError, self).__init__(error_msg)
|
|
|
|
|
|
class RolloutIDExhaustedError(exceptions.Error):
|
|
"""Error when there are too many rollouts for a given release."""
|
|
|
|
def __init__(self, release_name):
|
|
super(RolloutIDExhaustedError, self).__init__(
|
|
'Rollout name space exhausted in release {}. Use --rollout-id to '
|
|
'specify rollout ID.'.format(release_name)
|
|
)
|
|
|
|
|
|
class RolloutInProgressError(exceptions.Error):
|
|
"""Error when there is a rollout in progress, no to-target value is given and a promote is attempted."""
|
|
|
|
def __init__(self, release_name, target_name):
|
|
super(RolloutInProgressError, self).__init__(
|
|
'Unable to promote release {} to target {}. '
|
|
'A rollout is already in progress.'.format(release_name, target_name)
|
|
)
|
|
|
|
|
|
class RolloutNotInProgressError(exceptions.Error):
|
|
"""Error when a rollout is not in_progress, but is expected to be."""
|
|
|
|
def __init__(self, rollout_name):
|
|
super(RolloutNotInProgressError, self).__init__(
|
|
'Rollout {} is not IN_PROGRESS.'.format(rollout_name)
|
|
)
|
|
|
|
|
|
class RolloutCannotAdvanceError(exceptions.Error):
|
|
"""Error when a rollout cannot be advanced because of a failed precondition."""
|
|
|
|
def __init__(self, rollout_name, failed_activity_msg):
|
|
error_msg = '{} Rollout {} cannot be advanced.'.format(
|
|
failed_activity_msg, rollout_name
|
|
)
|
|
super(RolloutCannotAdvanceError, self).__init__(error_msg)
|
|
|
|
|
|
class PipelineSuspendedError(exceptions.Error):
|
|
"""Error when a user performs an activity on a suspended pipeline."""
|
|
|
|
def __init__(self, pipeline_name, failed_activity_msg):
|
|
error_msg = '{} DeliveryPipeline {} is suspended.'.format(
|
|
failed_activity_msg, pipeline_name
|
|
)
|
|
super(PipelineSuspendedError, self).__init__(error_msg)
|
|
|
|
|
|
class AutomationNameFormatError(exceptions.Error):
|
|
"""Error when the name of the automation in the config file is not formatted correctly."""
|
|
|
|
def __init__(self, automation_name):
|
|
super(AutomationNameFormatError, self).__init__(
|
|
'Automation name {} in the configuration should be in the format'
|
|
' of pipeline_id/automation_id.'.format(automation_name)
|
|
)
|
|
|
|
|
|
class AutomationWaitFormatError(exceptions.Error):
|
|
"""Error when the name of the automation in the config file is not formatted correctly."""
|
|
|
|
def __init__(self):
|
|
super(AutomationWaitFormatError, self).__init__(
|
|
'Wait must be numbers with the last character m, e.g. 5m.'
|
|
)
|
|
|
|
|
|
class MissingCoupledArgumentsException(c_exceptions.ToolException):
|
|
"""An exception for when only one of several arguments that need to be passed together is passed."""
|
|
|
|
def __init__(self, parameter_names):
|
|
super(MissingCoupledArgumentsException, self).__init__(
|
|
f'All of these flags {parameter_names} must be supplied together'
|
|
)
|