예제 #1
0
    def __PushServiceConfigFiles(self,
                                 files,
                                 service_name,
                                 project_id,
                                 config_id,
                                 normalized=False):
        """Creates a new ServiceConfig in Service Management.

    Args:
      files: Files to be pushed to Service Management
      service_name: Name of the service to push configs to
      project_id: Project the service belongs to
      config_id: ID to assign to the new ServiceConfig
      normalized: Whether or not this is a normalized google service

    Returns:
      ServiceConfig Id
    """
        if normalized:
            config_contents = endpoints.ReadServiceConfigFile(files[0])
            push_config_result = endpoints.PushNormalizedGoogleServiceConfig(
                service_name,
                project_id,
                endpoints.LoadJsonOrYaml(config_contents),
                config_id=config_id)
            service_config_id = push_config_result.id
        else:
            push_config_result = endpoints.PushMultipleServiceConfigFiles(
                service_name, files, False, config_id=config_id)
            service_config_id = (
                endpoints.GetServiceConfigIdFromSubmitConfigSourceResponse(
                    push_config_result))

        return service_config_id
예제 #2
0
  def Run(self, args):
    """Run 'endpoints services deploy'.

    Args:
      args: argparse.Namespace, The arguments that this command was invoked
          with.

    Returns:
      The response from the Update API call.

    Raises:
      BadFileExceptionn: if the provided service configuration files are
          invalid or cannot be read.
    """
    messages = services_util.GetMessagesModule()
    client = services_util.GetClientInstance()

    file_types = messages.ConfigFile.FileTypeValueValuesEnum
    self.service_name = self.service_version = config_contents = None
    config_files = []

    self.validate_only = args.validate_only

    # TODO(b/77867100): remove .proto support and deprecation warning.
    give_proto_deprecate_warning = False

    # If we're not doing a validate-only run, we don't want to output the
    # resource directly unless the user specifically requests it using the
    # --format flag. The Epilog will show useful information after deployment
    # is complete.
    if not self.validate_only and not args.IsSpecified('format'):
      args.format = 'none'

    for service_config_file in args.service_config_file:
      config_contents = services_util.ReadServiceConfigFile(service_config_file)

      if services_util.FilenameMatchesExtension(
          service_config_file, ['.json', '.yaml', '.yml']):
        # Try to load the file as JSON. If that fails, try YAML.
        service_config_dict = services_util.LoadJsonOrYaml(config_contents)
        if not service_config_dict:
          raise calliope_exceptions.BadFileException(
              'Could not read JSON or YAML from service config file '
              '[{0}].'.format(service_config_file))

        if 'swagger' in service_config_dict:
          if 'host' not in service_config_dict:
            raise calliope_exceptions.BadFileException((
                'Malformed input. Found Swagger service config in file [{}], '
                'but no host was specified. Add a host specification to the '
                'config file.').format(
                    service_config_file))
          if not self.service_name and service_config_dict.get('host'):
            self.service_name = service_config_dict.get('host')

          # Always use YAML for Open API because JSON is a subset of YAML.
          config_files.append(
              self.MakeConfigFileMessage(config_contents, service_config_file,
                                         file_types.OPEN_API_YAML))
        elif service_config_dict.get('type') == 'google.api.Service':
          if not self.service_name and service_config_dict.get('name'):
            self.service_name = service_config_dict.get('name')

          config_files.append(
              self.MakeConfigFileMessage(config_contents, service_config_file,
                                         file_types.SERVICE_CONFIG_YAML))
        elif 'name' in service_config_dict:
          # This is a special case. If we have been provided a Google Service
          # Configuration file which has a service 'name' field, but no 'type'
          # field, we have to assume that this is a normalized service config,
          # and can be uploaded via the CreateServiceConfig API. Therefore,
          # we can short circute the process here.
          if len(args.service_config_file) > 1:
            raise calliope_exceptions.BadFileException((
                'Ambiguous input. Found normalized service configuration in '
                'file [{0}], but received multiple input files. To upload '
                'normalized service config, please provide it separately from '
                'other input files to avoid ambiguity.').format(
                    service_config_file))

          # If this is a validate-only run, abort now, since this is not
          # supported in the ServiceConfigs.Create API
          if self.validate_only:
            raise exceptions.InvalidFlagError(
                'The --validate-only flag is not supported when using '
                'normalized service configs as input.')

          self.service_name = service_config_dict.get('name')
          config_files = []
          break
        else:
          raise calliope_exceptions.BadFileException((
              'Unable to parse Open API, or Google Service Configuration '
              'specification from {0}').format(service_config_file))

      elif services_util.IsProtoDescriptor(service_config_file):
        config_files.append(
            self.MakeConfigFileMessage(config_contents, service_config_file,
                                       file_types.FILE_DESCRIPTOR_SET_PROTO))
      elif services_util.IsRawProto(service_config_file):
        give_proto_deprecate_warning = True
        config_files.append(
            self.MakeConfigFileMessage(config_contents, service_config_file,
                                       file_types.PROTO_FILE))
      else:
        raise calliope_exceptions.BadFileException((
            'Could not determine the content type of file [{0}]. Supported '
            'extensions are .json .yaml .yml .pb and .descriptor').format(
                service_config_file))

    if give_proto_deprecate_warning:
      log.warning(
          'Support for uploading uncompiled .proto files is deprecated and '
          'will soon be removed. Use compiled descriptor sets (.pb) instead.\n')

    # Check if we need to create the service.
    was_service_created = False
    if not services_util.DoesServiceExist(self.service_name):
      project_id = properties.VALUES.core.project.Get(required=True)
      # Deploying, even with validate-only, cannot succeed without the service
      # being created
      if self.validate_only:
        if not console_io.CanPrompt():
          raise exceptions.InvalidConditionError(VALIDATE_NEW_ERROR.format(
              service_name=self.service_name, project_id=project_id))
        if not console_io.PromptContinue(
            VALIDATE_NEW_PROMPT.format(
                service_name=self.service_name, project_id=project_id)):
          return None
      services_util.CreateService(self.service_name, project_id)
      was_service_created = True

    if config_files:
      push_config_result = services_util.PushMultipleServiceConfigFiles(
          self.service_name, config_files, args.async_,
          validate_only=self.validate_only)
      self.service_config_id = (
          services_util.GetServiceConfigIdFromSubmitConfigSourceResponse(
              push_config_result)
      )
    else:
      push_config_result = services_util.PushNormalizedGoogleServiceConfig(
          self.service_name,
          properties.VALUES.core.project.Get(required=True),
          services_util.LoadJsonOrYaml(config_contents))
      self.service_config_id = push_config_result.id

    if not self.service_config_id:
      raise exceptions.InvalidConditionError(
          'Failed to retrieve Service Configuration Id.')

    # Run the Push Advisor to see if we need to warn the user of any
    # potentially hazardous changes to the service configuration.
    if self.CheckPushAdvisor(args.force):
      return None

    # Create a Rollout for the new service configuration
    if not self.validate_only:
      percentages = messages.TrafficPercentStrategy.PercentagesValue()
      percentages.additionalProperties.append(
          (messages.TrafficPercentStrategy.PercentagesValue.AdditionalProperty(
              key=self.service_config_id, value=100.0)))
      traffic_percent_strategy = messages.TrafficPercentStrategy(
          percentages=percentages)
      rollout = messages.Rollout(
          serviceName=self.service_name,
          trafficPercentStrategy=traffic_percent_strategy,)
      rollout_create = messages.ServicemanagementServicesRolloutsCreateRequest(
          rollout=rollout,
          serviceName=self.service_name,
      )
      rollout_operation = client.services_rollouts.Create(rollout_create)
      services_util.ProcessOperationResult(rollout_operation, args.async_)

      if was_service_created:
        self.AttemptToEnableService(self.service_name, args.async_)

    return push_config_result