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
def __ValidJsonOrYaml(self, file_name, file_contents): """Whether or not this is a valid json or yaml file. Args: file_name: Name of the file file_contents: data for the file Returns: Boolean for whether or not this is a JSON or YAML Raises: BadFileException: File appears to be json or yaml but cannot be parsed. """ if endpoints.FilenameMatchesExtension(file_name, ['.json', '.yaml', '.yml']): config_dict = endpoints.LoadJsonOrYaml(file_contents) if config_dict: return config_dict else: raise calliope_exceptions.BadFileException( 'Could not read JSON or YAML from config file ' '[{0}].'.format(file_name)) else: return False
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 # 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)) 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)) # Check to see if the Endpoints meta service needs to be enabled. enable_api.EnableServiceIfDisabled( properties.VALUES.core.project.Get(required=True), services_util.GetEndpointsServiceName(), args. async) # Check if we need to create the service. services_util.CreateServiceIfNew( self.service_name, properties.VALUES.core.project.Get(required=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), 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) # Check to see if the service is already enabled enable_api.EnableServiceIfDisabled( properties.VALUES.core.project.Get(required=True), self.service_name, args. async) return push_config_result