def Run(self, args): """Run 'service-management delete'. Args: args: argparse.Namespace, The arguments that this command was invoked with. Returns: The response from the Delete API call (or None if cancelled). """ messages = services_util.GetMessagesModule() client = services_util.GetClientInstance() # Prompt with a warning before continuing. console_io.PromptContinue( message= 'Are you sure? This will set the service configuration to be ' 'deleted, along with all of the associated consumer ' 'information. Note: This does not immediately delete the ' 'service configuration or data and can be undone using the ' 'undelete command for 30 days. Only after 30 days will the ' 'service be purged from the system.', prompt_string='Continue anyway', default=True, throw_if_unattended=True, cancel_on_no=True) service = arg_parsers.GetServiceNameFromArg(args.service) request = messages.ServicemanagementServicesDeleteRequest( serviceName=service, ) operation = client.services.Delete(request) return services_util.ProcessOperationResult(operation, args. async)
def EnableServiceApiCall(project_id, service_name): """Make API call to enable a specific API. Args: project_id: The ID of the project for which to enable the service. service_name: The name of the service to enable on the project. Raises: services_util.EnableServicePermissionDeniedException: when enabling the API fails. api_lib_exceptions.HttpException: Another miscellaneous error with the enabling service. Returns: The result of the Enable operation """ client = services_util.GetClientInstance() messages = services_util.GetMessagesModule() request = messages.ServicemanagementServicesEnableRequest( serviceName=service_name, enableServiceRequest=messages.EnableServiceRequest( consumerId='project:' + project_id)) try: return client.services.Enable(request) except exceptions.HttpError as e: if e.status_code in [403, 404]: # TODO(b/36865980): When backend supports it, differentiate errors. msg = json.loads(e.content).get('error', {}).get('message', '') raise services_util.EnableServicePermissionDeniedException(msg) else: raise api_lib_exceptions.HttpException(e)
def Run(self, args): """Run 'service-management remove-iam-policy-binding'. Args: args: argparse.Namespace, The arguments that this command was invoked with. Returns: The response from the access API call. Raises: ToolException: An error such as specifying a label that doesn't exist or a principal that is already a member of the service or visibility label. """ messages = services_util.GetMessagesModule() client = services_util.GetClientInstance() request = messages.ServicemanagementServicesGetIamPolicyRequest( servicesId=args.service) policy = client.services.GetIamPolicy(request) iam_util.RemoveBindingFromIamPolicy(policy, args.member, args.role) # Send updated access policy to backend request = messages.ServicemanagementServicesSetIamPolicyRequest( servicesId=args.service, setIamPolicyRequest=messages.SetIamPolicyRequest(policy=policy)) return client.services.SetIamPolicy(request)
def EnableServiceApiCall(project_id, service_name): """Make API call to enable a specific API. Args: project_id: The ID of the project for which to enable the service. service_name: The name of the service to enable on the project. Raises: exceptions.EnableServicePermissionDeniedException: when enabling the API fails. apitools_exceptions.HttpError: Another miscellaneous error with the enabling service. Returns: The result of the Enable operation """ client = services_util.GetClientInstance() messages = services_util.GetMessagesModule() request = messages.ServicemanagementServicesEnableRequest( serviceName=service_name, enableServiceRequest=messages.EnableServiceRequest( consumerId='project:' + project_id)) try: return client.services.Enable(request) except (apitools_exceptions.HttpForbiddenError, apitools_exceptions.HttpNotFoundError) as e: # TODO(b/36865980): When backend supports it, differentiate errors. _ReraiseError(e, exceptions.EnableServicePermissionDeniedException)
def Run(self, args): """Run 'service-management operations describe'. Args: args: argparse.Namespace, The arguments that this command was invoked with. Returns: The response from the operations.Get API call. """ messages = services_util.GetMessagesModule() client = services_util.GetClientInstance() # If a user includes the leading "operations/", just strip it off if args.operation.startswith(OPTIONAL_PREFIX_TO_STRIP): args.operation = args.operation[len(OPTIONAL_PREFIX_TO_STRIP):] request = messages.ServicemanagementOperationsGetRequest( operationsId=args.operation,) operation = client.operations.Get(request) if (sys.getsizeof(str(operation.response)) > MAX_RESPONSE_BYTES and not args.full): log.warn('Response portion of operation resource redacted. ' 'Use --full to see the whole Operation.\n') operation.response = None # Set async to True because we don't need to wait for the operation # to complete to check the status of it. return services_util.GetProcessedOperationResult(operation, async=True)
def Run(self, args): """Run 'service-management configs list'. Args: args: argparse.Namespace, The arguments that this command was invoked with. Returns: The response from the List API call. """ messages = services_util.GetMessagesModule() client = services_util.GetClientInstance() service = arg_parsers.GetServiceNameFromArg(args.service) request = messages.ServicemanagementServicesConfigsListRequest( serviceName=service) return list_pager.YieldFromList( client.services_configs, request, limit=args.limit, batch_size_attribute='pageSize', batch_size=args.page_size, field='serviceConfigs')
def Run(self, args): """Run 'service-management check-access'. Args: args: argparse.Namespace, The arguments that this command was invoked with. Returns: The response from the access API call. """ messages = services_util.GetMessagesModule() client = services_util.GetClientInstance() all_iam_permissions = [ 'servicemanagement.services.get', 'servicemanagement.services.getProjectSettings', 'servicemanagement.services.delete', 'servicemanagement.services.update', 'servicemanagement.services.use', 'servicemanagement.services.updateProjectSettings', 'servicemanagement.services.check', 'servicemanagement.services.report', 'servicemanagement.services.setIamPolicy', 'servicemanagement.services.getIamPolicy', ] # Shorten the query request name for better readability query_request = messages.ServicemanagementServicesTestIamPermissionsRequest request = query_request( servicesId=args.service, testIamPermissionsRequest=messages.TestIamPermissionsRequest( permissions=all_iam_permissions)) return client.services.TestIamPermissions(request)
def Run(self, args): """Run 'service-management check-access'. Args: args: argparse.Namespace, The arguments that this command was invoked with. Returns: The response from the access API call. """ messages = services_util.GetMessagesModule() client = services_util.GetClientInstance() all_iam_permissions = services_util.ALL_IAM_PERMISSIONS # Shorten the query request name for better readability query_request = messages.ServicemanagementServicesTestIamPermissionsRequest service = arg_parsers.GetServiceNameFromArg(args.service) request = query_request( servicesId=service, testIamPermissionsRequest=messages.TestIamPermissionsRequest( permissions=all_iam_permissions)) return client.services.TestIamPermissions(request)
def EnableServiceApiCall(project_id, service_name): """Make API call to enable a specific API. Args: project_id: The ID of the project for which to enable the service. service_name: The name of the service to enable on the project. Raises: exceptions.EnableServicePermissionDeniedException: when enabling the API fails. api_lib_exceptions.HttpException: Another miscellaneous error with the enabling service. Returns: The result of the Enable operation """ client = services_util.GetClientInstance() messages = services_util.GetMessagesModule() request = messages.ServicemanagementServicesEnableRequest( serviceName=service_name, enableServiceRequest=messages.EnableServiceRequest( consumerId='project:' + project_id)) try: return client.services.Enable(request) except apitools_exceptions.HttpError as e: _HandleStatusCode(e, exceptions.EnableServicePermissionDeniedException)
def Run(self, args): """Run 'service-management operations list'. Args: args: argparse.Namespace, The arguments that this command was invoked with. Returns: The list of operations for this project. """ messages = services_util.GetMessagesModule() client = services_util.GetClientInstance() service = arg_parsers.GetServiceNameFromArg(args.service) msg_filter = 'serviceName="{0}"'.format(service) if args.filter: msg_filter += ' AND ({0})'.format(args.filter) args.filter = None # We don't want Display() to handle the filter. msg = messages.ServicemanagementOperationsListRequest(filter=msg_filter) return list_pager.YieldFromList( client.operations, msg, limit=args.limit, batch_size_attribute='pageSize', batch_size=args.page_size, field='operations')
def _GetConfig(self, service, config_id): messages = services_util.GetMessagesModule() client = services_util.GetClientInstance() request = messages.ServicemanagementServicesConfigsGetRequest( serviceName=service, configId=config_id) return client.services_configs.Get(request)
def MakeConfigFile(self, file_contents, filename, file_type): messages = services_util.GetMessagesModule() return messages.ConfigFile( fileContents=file_contents, filePath=os.path.basename(filename), fileType=file_type, )
def _GetLatestVersionConfig(self, service): messages = services_util.GetMessagesModule() client = services_util.GetClientInstance() request = messages.ServicemanagementServicesGetRequest( serviceName=service, expand='service_config') service_result = client.services.Get(request) # Return the service config from the service result return service_result.serviceConfig
def Run(self, args): """Run 'service-management convert-config'. Args: args: argparse.Namespace, The arguments that this command was invoked with. Returns: The response from the ConvertConfig API call. Raises: IOError: An IOError is returned if the input file cannot be read, or the output file cannot be written to. """ log.warn('This command is deprecated and will be removed soon.') messages = services_util.GetMessagesModule() client = services_util.GetClientInstance() # TODO(b/36057355): Add support for swagger file references later # This requires the API to support multiple files first. b/23353397 try: with open(args.open_api_file) as f: open_api_spec = messages.OpenApiSpec(openApiFiles=[ messages.ConfigFile(filePath=os.path.basename( args.open_api_file), contents=f.read()) ]) except IOError: raise calliope_exceptions.NewErrorFromCurrentException( exceptions.FileOpenError, 'Cannot open {f} file'.format(f=args.open_api_file)) request = messages.ConvertConfigRequest(openApiSpec=open_api_spec) converted_config = client.v1.ConvertConfig(request) diagnostics = converted_config.diagnostics if diagnostics: kind = messages.Diagnostic.KindValueValuesEnum for diagnostic in diagnostics: logger = log.error if diagnostic.kind == kind.ERROR else log.warning logger('{l}: {m}'.format(l=diagnostic.location, m=diagnostic.message)) service = converted_config.serviceConfig if service: if args.output_file: try: with open(args.output_file, 'w') as out: out.write(encoding.MessageToJson(service)) except IOError: raise calliope_exceptions.NewErrorFromCurrentException( exceptions.FileOpenError, 'Cannot open output file \'{f}\''.format( f=args.output_file)) else: return service
def EnableServiceApiCall(project_id, service_name): """Make API call to enable a specific API.""" client = services_util.GetClientInstance() messages = services_util.GetMessagesModule() request = messages.ServicemanagementServicesEnableRequest( serviceName=service_name, enableServiceRequest=messages.EnableServiceRequest( consumerId='project:' + project_id)) return client.services.Enable(request)
def MakeConfigFileMessage(self, file_contents, filename, file_type): """Constructs a ConfigFile message from a config file. Args: file_contents: The contents of the config file. filename: The full path to the config file. file_type: FileTypeValueValuesEnum describing the type of config file. Returns: The constructed ConfigFile message. """ messages = services_util.GetMessagesModule() return messages.ConfigFile( fileContents=file_contents, filePath=os.path.basename(filename), fileType=file_type,)
def Run(self, args): """Run 'service-management describe'. Args: args: argparse.Namespace, The arguments that this command was invoked with. Returns: The response from the Get API call. """ messages = services_util.GetMessagesModule() client = services_util.GetClientInstance() request = messages.ServicemanagementServicesGetRequest( serviceName=args.service, ) return client.services.Get(request)
def Run(self, args): """Run 'service-management undelete'. Args: args: argparse.Namespace, The arguments that this command was invoked with. Returns: The response from the Undelete API call (or None if cancelled). """ messages = services_util.GetMessagesModule() client = services_util.GetClientInstance() request = messages.ServicemanagementServicesUndeleteRequest( serviceName=args.service, ) operation = client.services.Undelete(request) return services_util.ProcessOperationResult(operation, args. async)
def EnableServiceApiCall(project_id, service_name): """Make API call to enable a specific API. Args: project_id: The ID of the project for which to enable the service. service_name: The name of the service to enable on the project. Returns: The result of the Enable operation """ client = services_util.GetClientInstance() messages = services_util.GetMessagesModule() request = messages.ServicemanagementServicesEnableRequest( serviceName=service_name, enableServiceRequest=messages.EnableServiceRequest( consumerId='project:' + project_id)) return client.services.Enable(request)
def Run(self, args): """Run 'service-management disable'. Args: args: argparse.Namespace, The arguments that this command was invoked with. Returns: The response from the consumer settings API call. """ messages = services_util.GetMessagesModule() client = services_util.GetClientInstance() project = properties.VALUES.core.project.Get(required=True) request = messages.ServicemanagementServicesDisableRequest( serviceName=args.service, disableServiceRequest=messages.DisableServiceRequest( consumerId='project:' + project)) operation = client.services.Disable(request) return services_util.ProcessOperationResult(operation, args. async)
def Run(self, args): """Run 'service-management add-iam-policy-binding'. Args: args: argparse.Namespace, The arguments that this command was invoked with. Returns: The response from the access API call. Raises: ToolException: An error such as specifying a label that doesn't exist or a principal that is already a member of the service or visibility label. """ messages = services_util.GetMessagesModule() client = services_util.GetClientInstance() service = arg_parsers.GetServiceNameFromArg(args.service) request = messages.ServicemanagementServicesGetIamPolicyRequest( servicesId=service) try: policy = client.services.GetIamPolicy(request) except apitools_exceptions.HttpError as error: # If the error is a 404, no IAM policy exists, so just create a blank one. exc = exceptions.HttpException(error) if exc.payload.status_code == 404: policy = messages.Policy() else: raise iam_util.AddBindingToIamPolicy(messages.Binding, policy, args.member, args.role) # Send updated access policy to backend request = messages.ServicemanagementServicesSetIamPolicyRequest( servicesId=service, setIamPolicyRequest=(messages.SetIamPolicyRequest(policy=policy))) return client.services.SetIamPolicy(request)
def Run(self, args): """Run 'service-management operations wait'. Args: args: argparse.Namespace, The arguments that this command was invoked with. Returns: If successful, the response from the operations.Get API call. """ messages = services_util.GetMessagesModule() client = services_util.GetClientInstance() operation_id = arg_parsers.GetOperationIdFromArg(args.operation) request = messages.ServicemanagementOperationsGetRequest( operationsId=operation_id, ) operation = client.operations.Get(request) return services_util.ProcessOperationResult(operation, async=False)
def Run(self, args): """Run 'service-management get-iam-policy'. Args: args: argparse.Namespace, The arguments that this command was invoked with. Returns: The response from the access API call. Raises: HttpException: An http error response was received while executing api request. """ messages = services_util.GetMessagesModule() client = services_util.GetClientInstance() request = messages.ServicemanagementServicesGetIamPolicyRequest( servicesId=args.service) return client.services.GetIamPolicy(request)
def Run(self, args): """Run 'service-management deploy'. Args: args: argparse.Namespace, The arguments that this command was invoked with. Returns: The response from the Update API call. Raises: SwaggerUploadException: if the provided service configuration files are rejected by the Service Management API. 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 = [] for service_config_file in args.service_config_file: config_contents = None try: with open(service_config_file, 'r') as f: config_contents = f.read() except IOError as ex: raise calliope_exceptions.BadFileException( 'Could not open service config file [{0}]: {1}'.format( service_config_file, ex)) if self.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 not self.service_name: self.service_name = service_config_dict.get( 'host', None) # Always use YAML for Open API because JSON is a subset of YAML. config_files.append( self.MakeConfigFile(config_contents, service_config_file, file_types.OPEN_API_YAML)) elif service_config_dict.get('type') == 'google.api.Service': self.service_name = service_config_dict.get('name') config_files.append( self.MakeConfigFile(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)) self.service_name = service_config_dict.get('name', None) 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 self.FilenameMatchesExtension(service_config_file, ['.pb', '.descriptor']): config_files.append( self.MakeConfigFile(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: self.service_config_id = services_util.PushMultipleServiceConfigFiles( self.service_name, config_files, args. async) else: self.service_config_id = services_util.PushNormalizedGoogleServiceConfig( self.service_name, properties.VALUES.core.project.Get(required=True), config_contents) if not self.service_config_id: raise calliope_exceptions.ToolException( 'Failed to retrieve Service Configuration Id.') # Create a Rollout for the new service configuration 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_operation = client.services_rollouts.Create(rollout) 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)
client = services_util.GetClientInstance() messages = services_util.GetMessagesModule() request = messages.ServicemanagementServicesEnableRequest( serviceName=service_name, enableServiceRequest=messages.EnableServiceRequest( consumerId='project:' + project_id)) return client.services.Enable(request) @http_error_handler.HandleHttpErrors def EnableServiceIfDisabled(project_id, service_name, async=False): """Check to see if the service is enabled, and if it is not, do so.""" client = services_util.GetClientInstance() messages = services_util.GetMessagesModule() # Check to see if the service is already enabled request = messages.ServicemanagementServicesProjectSettingsGetRequest( serviceName=service_name, consumerProjectId=project_id, view=services_util.GetCallerViews().get('CONSUMER')) project_settings_result = client.services_projectSettings.Get(request) enabled = messages.UsageSettings.ConsumerEnableStatusValueValuesEnum.ENABLED # If the service is not yet enabled, enable it if (not project_settings_result.usageSettings or project_settings_result.usageSettings.consumerEnableStatus != enabled): log.status.Print('Enabling service {0} on project {1}...'.format(
def services_messages(self): return services_util.GetMessagesModule()
def Run(self, args): """Run 'service-management 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_operation = client.services_rollouts.Create(rollout) 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