| #!/usr/bin/env python |
| # |
| # Copyright 2007 Google Inc. |
| # |
| # 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 related to Managed VM deployments.""" |
| |
| import logging |
| |
| |
| class Error(Exception): |
| """Base error class for google.appengine.client.services.""" |
| pass |
| |
| |
| class PermanentAppError(Error): |
| """Base class for app config related errors we are unable to recover from. |
| |
| Handlers should catch this type of errors since retrying is of no use. |
| """ |
| |
| def __init__(self, end_user_message, internal_message=None): |
| """Constructor. |
| |
| Args: |
| end_user_message: The message to forward to appcfg so it can be |
| displayed to the end user. |
| internal_message: (optional) Error message to log as error. |
| """ |
| # Use the base Exception class message field to store the end_user_message. |
| super(PermanentAppError, self).__init__(end_user_message) |
| self.internal_message = internal_message |
| |
| def LogError(self): |
| logging.debug('End user message: %s', self.message) |
| if self.internal_message: |
| logging.error(self.internal_message) |
| |
| |
| # TODO: Some of these exceptions specify meaningful error messages, while |
| # others rely on the caller to do so. We should be consistent about this. |
| |
| # TODO: Some exceptions here are specific to particular modules, |
| # while some are generic and applicable across modules. On one hand, if |
| # module-specific Exceptions were defined in their respective modules, there is |
| # no risk of misapplying them in improper places. On the other hand, keeping |
| # all errors in one file is convenient, and allows us to avoid having to disable |
| # catching-non-exception lint errors (caused because pylint doesn't trace the |
| # inheritance across files). |
| |
| |
| class TransientError(Error): |
| """Base class for transient errors that should lead to a retry of the task.""" |
| |
| |
| class GlobalConfigLoadError(TransientError): |
| """Thrown if there was an error when loading the GlobalConfig for the app.""" |
| |
| |
| class RobotSetupError(PermanentAppError): |
| """Thrown if there was an error with the robot setup.""" |
| |
| |
| class InvalidBucketConfigurationError(PermanentAppError): |
| """Thrown if there is no GCS bucket configured in AdminConfig.""" |
| |
| def __init__(self): |
| # TODO: Improve this with doc links / next steps the user can take. |
| PermanentAppError.__init__( |
| self, |
| 'Severe: This app has no associated Google Cloud Storage bucket.', |
| 'Configuration error: Neither admin_config.vm_config_bigstore_bucket' |
| 'nor admin_config.default_bigstore_bucket is set. Deployment aborted.') |
| |
| |
| class InvalidUserInstallItem(PermanentAppError): |
| """Thrown if one of the apt_get_install items is invalid.""" |
| |
| def __init__(self, bad_package): |
| msg = 'Invalid apt-get-install package in vm_settings:"%s"' % bad_package |
| PermanentAppError.__init__(self, msg) |
| |
| |
| class InvalidScopeConfigurationError(PermanentAppError): |
| """Thrown if there are incompatible storage scopes in vm_settings.""" |
| |
| def __init__(self): |
| PermanentAppError.__init__( |
| self, |
| 'Severe: Invalid storage scope configuration. ' |
| 'Only one of devstorage.read_only, devstorage.read_write, ' |
| 'devstorage.full_control can be used at a time.') |
| |
| |
| class InvalidRootSetupCommandError(PermanentAppError): |
| """Thrown if the root_setup_command format is invalid.""" |
| |
| def __init__(self): |
| PermanentAppError.__init__( |
| self, |
| 'Severe: Invalid root_setup_command format string.' |
| 'Invalid root_setup_command format string.') |
| |
| |
| class StaleGlobalConfigError(TransientError): |
| """Thrown if the GlobalConfig is older than the requester's.""" |
| |
| def __init__(self, msg): |
| TransientError.__init__(self, msg) |
| |
| |
| class AppLockError(PermanentAppError): |
| """Thrown if there was an error with the AppLock.""" |
| |
| def __init__(self): |
| PermanentAppError.__init__( |
| self, |
| 'An internal error occurred. Please retry.', |
| 'Unable to load the AppLock using the provided applock_key.') |
| |
| |
| class UnknownAppIdError(PermanentAppError): |
| """Thrown if the provided app id does not exist.""" |
| |
| |
| class UnknownVersionIdError(PermanentAppError): |
| """Thrown if the provided version id does not exist.""" |
| |
| |
| class AdminConfigLoadError(TransientError): |
| """Thrown if there was an error when loading the AdminConfig of the app.""" |
| |
| |
| class AppInfoLoadError(TransientError): |
| """Thrown if there was an error when loading the AppInfo for the app.""" |
| |
| |
| class GcsWriteError(TransientError): |
| """Thrown if there was an error when writing to cloud storage.""" |
| |
| |
| class InvalidLockdownzResponseError(TransientError): |
| """Thrown if a lockdownz request to the agent returns an invalid response.""" |
| |
| |
| class InvalidEnvVariableError(PermanentAppError): |
| """Thrown if an environment variable name or value cannot be supported.""" |
| |
| |
| class MachineTypeInfoNotFoundError(PermanentAppError): |
| """Thrown when the MachineTypeInfo is not found.""" |
| |
| |
| class MachineTypeInfoLoadError(TransientError): |
| """Thrown when the MachineTypeInfo can't be loaded.""" |
| |
| |
| class InvalidImageError(PermanentAppError): |
| """Thrown if the user requested image can not be found.""" |
| |
| |
| class InvalidVmSettingsError(PermanentAppError): |
| """Thrown if the VM settings dictionary contains an invalid key or Value.""" |
| |
| |
| class InvalidDiskSizeConfiguration(InvalidVmSettingsError): |
| """Thrown if the user requsted an invalid disk size.""" |
| |
| |
| class GoManifestError(PermanentAppError): |
| """Thrown if there is a problem with the Go manifest.""" |
| |
| |
| class ReplicaPoolTimeoutError(TransientError): |
| """Thrown if there was a timeout with a ReplicaPool operation.""" |
| |
| |
| class MigrateDuplicateZoneError(PermanentAppError): |
| """Thrown if attempted to migrating a replica pool to its existing zone.""" |
| |
| |
| class ReplicaPoolTransientError(TransientError): |
| """Thrown if there was an 5xx HttpError from the ReplicaPool service.""" |
| |
| |
| class InvalidInstanceError(PermanentAppError): |
| """Thrown by (un)lock method, if deployment or instance could not be found.""" |
| |
| |
| class LockOperationError(PermanentAppError): |
| """Thrown by (un)lock method, in case of a downstream failure.""" |
| |
| |
| class OperationAborted(PermanentAppError): |
| """Thrown if operation was aborted (AppLock was revoked). Used internally.""" |
| |
| |
| # Errors thrown by api utility classes |
| class OperationDidNotCompleteError(TransientError): |
| """Thrown if an operation did not go to DONE status in time.""" |
| pass |
| |
| |
| class OperationFailedError(PermanentAppError): |
| """Thrown if an operation threw an error.""" |
| |
| def __init__(self, message, errors): |
| super(OperationFailedError, self).__init__( |
| message, internal_message='Operation errors: %s' % str(errors)) |
| self.errors = errors |
| |
| |
| class ResourceAlreadyExistsError(PermanentAppError): |
| """Thrown if the resource being inserted already exists.""" |
| pass |
| |
| |
| class ResourceDoesNotExistError(PermanentAppError): |
| """Thrown if the resource being updated/deleted doesn't exist.""" |
| pass |