Exemple #1
0
  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')
Exemple #2
0
    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)
Exemple #8
0
  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())))
Exemple #9
0
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())))
Exemple #11
0
    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)