def Run(self, args): api_client = appengine_api_client.GetApiClientForTrack(self.ReleaseTrack()) all_services = api_client.ListServices() services = service_util.GetMatchingServices(all_services, args.services) errors = {} for service in services: ingress_traffic_allowed = ( api_client.messages.NetworkSettings .IngressTrafficAllowedValueValuesEnum.INGRESS_TRAFFIC_ALLOWED_ALL) if args.ingress == 'internal-only': ingress_traffic_allowed = ( api_client.messages.NetworkSettings .IngressTrafficAllowedValueValuesEnum .INGRESS_TRAFFIC_ALLOWED_INTERNAL_ONLY) elif args.ingress == 'internal-and-cloud-load-balancing': ingress_traffic_allowed = ( api_client.messages.NetworkSettings .IngressTrafficAllowedValueValuesEnum .INGRESS_TRAFFIC_ALLOWED_INTERNAL_AND_LB) try: operations_util.CallAndCollectOpErrors( api_client.SetIngressTrafficAllowed, service.id, ingress_traffic_allowed) except operations_util.MiscOperationError as err: errors[service.id] = six.text_type(err) if errors: combined_error_msg = 'Error updating service(s): ' for service, error_msg in errors.items(): combined_error_msg += '\n- %s\n %s' % (service, error_msg) raise IngressSettingError(combined_error_msg + '\n\n')
def Run(self, args): # TODO(b/36057452): This fails with "module/version does not exist" even # when it exists if the scaling mode is set to auto. It would be good # to improve that error message. api_client = appengine_api_client.GetApiClientForTrack( self.ReleaseTrack()) services = api_client.ListServices() versions = version_util.GetMatchingVersions( api_client.ListVersions(services), args.versions, args.service) if versions: fmt = 'list[title="Stopping the following versions:"]' resource_printer.Print(versions, fmt, out=log.status) console_io.PromptContinue(cancel_on_no=True) else: log.warn('No matching versions found.') errors = [] for version in sorted(versions): try: with progress_tracker.ProgressTracker( 'Stopping [{0}]'.format(version)): operations_util.CallAndCollectOpErrors( api_client.StopVersion, version.service, version.id, block=True) except operations_util.MiscOperationError as err: errors.append(str(err)) if errors: raise VersionsStopError('\n\n'.join(errors))
def _StopPreviousVersionIfApplies(old_default_version, api_client, wait_for_stop_version): """Stop the previous default version if applicable. Cases where a version will not be stopped: * If the previous default version is not serving, there is no need to stop it. * If the previous default version is an automatically scaled standard environment app, it cannot be stopped. Args: old_default_version: Version, The old default version to stop. api_client: appengine_api_client.AppengineApiClient to use to make requests. wait_for_stop_version: bool, indicating whether to wait for stop operation to finish. """ version_object = old_default_version.version status_enum = api_client.messages.Version.ServingStatusValueValuesEnum if version_object.servingStatus != status_enum.SERVING: log.info('Previous default version [{0}] not serving, so not stopping ' 'it.'.format(old_default_version)) return is_standard = not (version_object.vm or version_object.env == 'flex' or version_object.env == 'flexible') if (is_standard and not version_object.basicScaling and not version_object.manualScaling): log.info('Previous default version [{0}] is an automatically scaled ' 'standard environment app, so not stopping it.'.format( old_default_version)) return log.status.Print('Stopping version [{0}].'.format(old_default_version)) try: # Block only if wait_for_stop_version is true. # Waiting for stop the previous version to finish adds a long time # (reports of 2.5 minutes) to deployment. The risk is that if we don't wait, # the operation might fail and leave an old version running. But the time # savings is substantial. operations_util.CallAndCollectOpErrors( api_client.StopVersion, service_name=old_default_version.service, version_id=old_default_version.id, block=wait_for_stop_version) except operations_util.MiscOperationError as err: log.warning('Error stopping version [{0}]: {1}'.format( old_default_version, six.text_type(err))) log.warning( 'Version [{0}] is still running and you must stop or delete it ' 'yourself in order to turn it off. (If you do not, you may be ' 'charged.)'.format(old_default_version)) else: if not wait_for_stop_version: # TODO(b/318248525): Switch to refer to `gcloud app operations wait` when # available log.status.Print( 'Sent request to stop version [{0}]. This operation may take some time ' 'to complete. If you would like to verify that it succeeded, run:\n' ' $ gcloud app versions describe -s {0.service} {0.id}\n' 'until it shows that the version has stopped.'.format( old_default_version))
def testMethodFailsWithUnrelatedError(self): """Method fails with different err, check that it falls through.""" method = mock.Mock(side_effect=ValueError(14)) with self.assertRaises(ValueError): operations_util.CallAndCollectOpErrors(method, 'positional', param=123)
def Run(self, args): client = appengine_api_client.GetApiClientForTrack(self.ReleaseTrack()) if args.service: service = client.GetServiceResource(args.service) traffic_split = {} if service.split: for split in service.split.allocations.additionalProperties: traffic_split[split.key] = split.value services = [service_util.Service(client.project, service.id, traffic_split)] else: services = client.ListServices() all_versions = client.ListVersions(services) if args.version not in {v.id for v in all_versions}: if args.service: raise VersionsMigrateError('Version [{0}/{1}] does not exist.'.format( args.service, args.version)) else: raise VersionsMigrateError('Version [{0}] does not exist.'.format( args.version)) service_names = {v.service for v in all_versions if v.id == args.version} def WillBeMigrated(v): return (v.service in service_names and v.traffic_split and v.traffic_split > 0 and v.id != args.version) # All versions that will stop receiving traffic. versions_to_migrate = list(six.moves.filter(WillBeMigrated, all_versions)) for version in versions_to_migrate: short_name = '{0}/{1}'.format(version.service, version.id) promoted_name = '{0}/{1}'.format(version.service, args.version) log.status.Print('Migrating all traffic from version ' '[{0}] to [{1}]'.format( short_name, promoted_name)) console_io.PromptContinue(cancel_on_no=True) errors = {} for service in sorted(set([v.service for v in versions_to_migrate])): allocations = {args.version: 1.0} try: operations_util.CallAndCollectOpErrors( client.SetTrafficSplit, service, allocations, shard_by='ip', migrate=True) except (operations_util.MiscOperationError) as err: errors[service] = str(err) if errors: error_string = ('Issues migrating all traffic of ' 'service(s): [{0}]\n\n{1}'.format( ', '.join(list(errors.keys())), '\n\n'.join(list(errors.values())))) raise VersionsMigrateError(error_string)
def testMethodFailsWithOperationError(self): """Method fails with OperationError, check for our misc error.""" operation_err = operations_util.OperationError('custom msg') # Raised error must serialize to the same as the original op error err_str = re.escape(str(operation_err)) method = mock.Mock(side_effect=operation_err) with self.assertRaisesRegex(operations_util.MiscOperationError, err_str): operations_util.CallAndCollectOpErrors(method, 'positional', param=123)
def testMethodFailsWithHttpError(self): """Method fails with HttpError, check for misc error with friendly str.""" http_err = http_error.MakeHttpError() http_exception = api_exceptions.HttpException(http_err) # Raised error must serialize to the same as the original http exception err_str = re.escape(str(http_exception)) method = mock.Mock(side_effect=http_err) with self.assertRaisesRegex(operations_util.MiscOperationError, err_str): operations_util.CallAndCollectOpErrors(method, 'positional', param=123)
def Run(self, args): if args.migrate and len(args.splits) > 1: raise TrafficSplitError('The migrate flag can only be used with splits ' 'to a single version.') api_client = appengine_api_client.GetApiClientForTrack(self.ReleaseTrack()) all_services = api_client.ListServices() services = service_util.GetMatchingServices(all_services, args.services) allocations = service_util.ParseTrafficAllocations( args.splits, args.split_by) display_allocations = [] for service in services: for version, split in six.iteritems(allocations): display_allocations.append('{0}/{1}/{2}: {3}'.format( api_client.project, service.id, version, split)) fmt = 'list[title="Setting the following traffic allocation:"]' resource_printer.Print(display_allocations, fmt, out=log.status) log.status.Print( 'NOTE: Splitting traffic by {0}.'.format(args.split_by)) log.status.Print('Any other versions of the specified service will ' 'receive zero traffic.') console_io.PromptContinue(cancel_on_no=True) errors = {} for service in services: try: operations_util.CallAndCollectOpErrors( api_client.SetTrafficSplit, service.id, allocations, args.split_by.upper(), args.migrate) except operations_util.MiscOperationError as err: errors[service.id] = str(err) if errors: printable_errors = {} for service, error_msg in errors.items(): printable_errors[service] = error_msg raise TrafficSplitError( 'Issue setting traffic on service(s): {0}\n\n'.format( ', '.join(list(printable_errors.keys()))) + '\n\n'.join(list(printable_errors.values())))
def DeleteServices(api_client, services): """Delete the given services.""" errors = {} for service in services: try: operations_util.CallAndCollectOpErrors(api_client.DeleteService, service.id) except operations_util.MiscOperationError as err: errors[service.id] = str(err) if errors: printable_errors = {} for service_id, error_msg in errors.items(): printable_errors[service_id] = '[{0}]: {1}'.format( service_id, error_msg) raise ServicesDeleteError('Issue deleting {0}: [{1}]\n\n'.format( text.Pluralize(len(printable_errors), 'service'), ', '.join( list(printable_errors.keys()))) + '\n\n'.join(list(printable_errors.values())))
def DeleteVersions(api_client, versions): """Delete the given version of the given services.""" errors = {} for version in versions: version_path = '{0}/{1}'.format(version.service, version.id) try: operations_util.CallAndCollectOpErrors(api_client.DeleteVersion, version.service, version.id) except operations_util.MiscOperationError as err: errors[version_path] = str(err) if errors: printable_errors = {} for version_path, error_msg in errors.items(): printable_errors[version_path] = '[{0}]: {1}'.format( version_path, error_msg) raise VersionsDeleteError('Issue deleting {0}: [{1}]\n\n'.format( text.Pluralize(len(printable_errors), 'version'), ', '.join( list(printable_errors.keys()))) + '\n\n'.join(list(printable_errors.values())))
def Run(self, args): # TODO(b/36052475): This fails with "module/version does not exist" even # when it exists if the scaling mode is set to auto. It would be good # to improve that error message. api_client = appengine_api_client.GetApiClientForTrack( self.ReleaseTrack()) services = api_client.ListServices() versions = version_util.GetMatchingVersions( api_client.ListVersions(services), args.versions, args.service) if not versions: log.warning('No matching versions found.') return fmt = 'list[title="Starting the following versions:"]' resource_printer.Print(versions, fmt, out=log.status) console_io.PromptContinue(cancel_on_no=True) errors = {} # Sort versions to make behavior deterministic enough for unit testing. for version in sorted(versions): try: with progress_tracker.ProgressTracker( 'Starting [{0}]'.format(version)): operations_util.CallAndCollectOpErrors( api_client.StartVersion, version.service, version.id) except operations_util.MiscOperationError as err: errors[version] = str(err) if errors: printable_errors = {} for version, error_msg in errors.items(): short_name = '[{0}/{1}]'.format(version.service, version.id) printable_errors[short_name] = '{0}: {1}'.format( short_name, error_msg) raise VersionsStartError( 'Issues starting version(s): {0}\n\n'.format(', '.join( printable_errors.keys())) + '\n\n'.join(printable_errors.values()))
def testMethodSucceeds(self): """Method succeeds, make sure it is called with the right args.""" method = mock.Mock(return_value='return_val') operations_util.CallAndCollectOpErrors(method, 'positional', param=123) method.assert_called_once_with('positional', param=123)