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 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 _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 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 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() operation_id = arg_parsers.GetOperationIdFromArg(args.operation) request = messages.ServicemanagementOperationsGetRequest( operationsId=operation_id,) operation = client.operations.Get(request) if (sys.getsizeof(str(operation.response)) > MAX_RESPONSE_BYTES and not args.full): log.warning('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, is_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 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() service = arg_parsers.GetServiceNameFromArg(args.service) request = messages.ServicemanagementServicesGetIamPolicyRequest( servicesId=service) policy = client.services.GetIamPolicy(request) iam_util.RemoveBindingFromIamPolicy(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 __DeleteEndpointsService(self, service_name): messages = endpoints.GetMessagesModule() client = endpoints.GetClientInstance() request = messages.ServicemanagementServicesDeleteRequest( serviceName=service_name) operation = client.services.Delete(request) return endpoints.ProcessOperationResult(operation, False)
def GenerateManagementUrl(service): """Generate a service management url for this service.""" # It is actually possible to deploy a service configuration for a service # which is not in the current project, as long as you have appropriate # permissions. Because of this, we need to explicitly query for the service's # project. messages = services_util.GetMessagesModule() client = services_util.GetClientInstance() get_request = messages.ServicemanagementServicesGetRequest( serviceName=service, ) response = client.services.Get(get_request) project = response.producerProjectId return ('https://console.cloud.google.com/endpoints/api/' '{service}/overview?project={project}'.format( service=six.moves.urllib.parse.quote(service), project=six.moves.urllib.parse.quote(project)))
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 __PushOpenApiServiceFile(self, open_api_specs, service_name, project_id, config_id): """Creates a new ServiceConfig in Service Management from OpenAPI spec. Args: open_api_specs: Specs 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 Returns: ServiceConfig Id Raises: BadFileException: If there is something wrong with the files """ messages = endpoints.GetMessagesModule() file_types = messages.ConfigFile.FileTypeValueValuesEnum config_files = [] for config_file in open_api_specs: config_contents = endpoints.ReadServiceConfigFile(config_file) config_dict = self.__ValidJsonOrYaml(config_file, config_contents) if config_dict: if 'swagger' in config_dict: # Always use YAML for OpenAPI because JSON is a subset of YAML. config_files.append( self.__MakeConfigFileMessage(config_contents, config_file, file_types.OPEN_API_YAML)) elif 'openapi' in config_dict: raise calliope_exceptions.BadFileException( 'API Gateway does not currently support OpenAPI v3 configurations.' ) else: raise calliope_exceptions.BadFileException( 'The file {} is not a valid OpenAPI v2 configuration file.' .format(config_file)) else: raise calliope_exceptions.BadFileException( 'OpenAPI files should be of JSON or YAML format') return self.__PushServiceConfigFiles(config_files, service_name, project_id, config_id)
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() service = arg_parsers.GetServiceNameFromArg(args.service) request = messages.ServicemanagementServicesGetRequest( serviceName=service, ) return client.services.Get(request)
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, is_async=False)
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() service = arg_parsers.GetServiceNameFromArg(args.service) request = messages.ServicemanagementServicesUndeleteRequest( serviceName=service, ) operation = client.services.Undelete(request) return services_util.ProcessOperationResult(operation, args. async)
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() file_types = messages.ConfigFile.FileTypeValueValuesEnum if file_type != file_types.FILE_DESCRIPTOR_SET_PROTO: # File is human-readable text, not binary; needs to be encoded. file_contents = http_encoding.Encode(file_contents) return messages.ConfigFile( fileContents=file_contents, filePath=os.path.basename(filename), fileType=file_type,)
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() service = arg_parsers.GetServiceNameFromArg(args.service) request = messages.ServicemanagementServicesGetIamPolicyRequest( servicesId=service) return client.services.GetIamPolicy(request)
def Run(self, args): """Run 'endpoints 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() operation_id = arg_parsers.GetOperationIdFromArg(args.operation) request = messages.ServicemanagementOperationsGetRequest( operationsId=operation_id,) operation = client.operations.Get(request) # 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, is_async=True)
def __PushGrpcConfigFiles(self, files, service_name, project_id, config_id): """Creates a new ServiceConfig in SerivceManagement from gRPC files. 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 Returns: ServiceConfig Id Raises: BadFileException: If there is something wrong with the files """ messages = endpoints.GetMessagesModule() file_types = messages.ConfigFile.FileTypeValueValuesEnum # TODO(b/77867100): remove .proto support and deprecation warning. give_proto_deprecate_warning = False config_files = [] for config_file in files: config_contents = endpoints.ReadServiceConfigFile(config_file) config_dict = self.__ValidJsonOrYaml(config_file, config_contents) if config_dict: if config_dict.get('type') == 'google.api.Service': config_files.append( self.__MakeConfigFileMessage( config_contents, config_file, file_types.SERVICE_CONFIG_YAML)) elif 'name' in 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(files) > 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(config_file)) return self.__PushServiceConfigFiles(files, service_name, project_id, config_id, normalized=True) else: raise calliope_exceptions.BadFileException( 'The file {} is not a valid api configuration file'. format(config_file)) elif endpoints.IsProtoDescriptor(config_file): config_files.append( self.__MakeConfigFileMessage( config_contents, config_file, file_types.FILE_DESCRIPTOR_SET_PROTO)) elif endpoints.IsRawProto(config_file): give_proto_deprecate_warning = True config_files.append( self.__MakeConfigFileMessage(config_contents, 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(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' ) return self.__PushServiceConfigFiles(config_files, service_name, project_id, config_id)
# limitations under the License. """Unit tests for endpoints services get-iam-policy command.""" from __future__ import absolute_import from __future__ import division from __future__ import unicode_literals from googlecloudsdk.api_lib.endpoints import services_util from googlecloudsdk.calliope import base as calliope_base from tests.lib import parameterized from tests.lib import test_case from tests.lib.surface.endpoints import unit_test_base GET_REQUEST = (services_util.GetMessagesModule() .ServicemanagementServicesGetIamPolicyRequest) # TODO(b/117336602) Stop using parameterized for track parameterization. @parameterized.parameters(calliope_base.ReleaseTrack.ALPHA, calliope_base.ReleaseTrack.BETA, calliope_base.ReleaseTrack.GA) class EndpointsGetIamPolicyTest(unit_test_base.EV1UnitTestBase): """Unit tests for endpoints services get-iam-policy command.""" def testGetIamPolicy(self, track): self.track = track bindings = [ self.services_messages.Binding( role='roles/servicemanagement.serviceConsumer', members=['user:[email protected]', 'user:[email protected]']),
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
# distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Unit tests for endpoints check-iam-policy command.""" from __future__ import absolute_import from __future__ import division from __future__ import unicode_literals from googlecloudsdk.api_lib.endpoints import services_util from tests.lib import test_case from tests.lib.surface.endpoints import unit_test_base TEST_REQUEST = (services_util.GetMessagesModule(). ServicemanagementServicesTestIamPermissionsRequest) _ALL_IAM_PERMISSIONS = [ 'servicemanagement.services.get', 'servicemanagement.services.getProjectSettings', 'servicemanagement.services.delete', 'servicemanagement.services.update', 'servicemanagement.services.bind', 'servicemanagement.services.updateProjectSettings', 'servicemanagement.services.check', 'servicemanagement.services.report', 'servicemanagement.services.setIamPolicy', 'servicemanagement.services.getIamPolicy', ]