def PromptToEnableApi(project, service_token, exception, is_batch_request=False): """Prompts to enable the API and throws if the answer is no. Args: project (str): The project that the API is not enabled on. service_token (str): The service token of the API to prompt for. exception (api_Exceptions.HttpException): Exception to throw if the prompt is denied. is_batch_request: If the request is a batch request. This determines how to get apitools to retry the request. Raises: api_exceptions.HttpException: API not enabled error if the user chooses to not enable the API. """ if console_io.PromptContinue( default=False, prompt_string=( 'API [{}] not enabled on project [{}]. ' 'Would you like to enable and retry (this will take a ' 'few minutes)?').format(service_token, project)): enable_api.EnableService(project, service_token) # In the case of a batch request, as long as the error's retryable code # (in this case 403) was set, after this runs it should retry. This # error code should be consistent with apis.GetApiEnablementInfo if not is_batch_request: raise apitools_exceptions.RequestError('Retry') else: raise exception
def SetUp(self): self.name_generator = e2e_utils.GetResourceNameGenerator('sdk-e2e') project_name = next(self.name_generator) self.project_id = command_lib_util.IdFromName(project_name) self.project_ref = command_lib_util.ParseProject(self.project_id) create_op = projects_api.Create( self.project_ref, parent=projects_api.ParentNameToResourceId( CLOUD_SDK_TESTING_FOLDER_ID)) log.CreatedResource(self.project_ref, is_async=True) operations.WaitForOperation(create_op) log.debug('Enabling cloudapis.googleapis.com') services_enable_api.EnableService(self.project_ref.Name(), 'cloudapis.googleapis.com') self.Run('services enable cloudbilling') self.Run(('alpha billing accounts projects link {project} ' '--account-id={billing}').format(project=self.project_id, billing=self.BillingId())) self.Run('projects add-iam-policy-binding {project} ' '--member="group:[email protected]" ' '--role="roles/owner"'.format(project=self.project_id)) properties.VALUES.core.disable_prompts.Set(True) # This is set to false by sdk_test_base.SdkBase. properties.VALUES.core.should_prompt_to_enable_api.Set(True) # The api enablement check will prompt, and this will inject a yes into that self.StartPatch( 'googlecloudsdk.core.console.console_io.PromptContinue', return_value=True)
def check_prerequisite(self): log.status.Print('---- Checking network connectivity ----') msg = ( 'The Network Management API is needed to check the VM\'s network ' 'connectivity.') prompt = 'Is it OK to enable it and check the VM\'s network connectivity?' cancel = ( 'Test skipped.\n' 'To manually test network connectivity, try reaching another ' 'device on the same network.\n') try: prompt_continue = console_io.PromptContinue(message=msg, prompt_string=prompt, cancel_on_no=True, cancel_string=cancel) self.skip_troubleshoot = not prompt_continue except OperationCancelledError: self.skip_troubleshoot = True if self.skip_troubleshoot: return # Enable API enable_api.EnableService(self.project.name, NETWORK_API) # Test IAM Permission missing_permissions = self._CheckNetworkManagementPermissions() if missing_permissions: log.status.Print( 'Missing the IAM permissions {0} necessary to perform the network ' 'connectivity test. To manually test network connectivity, try ' 'reaching another device on the same network.\n'.format( ' '.join(missing_permissions))) self.skip_troubleshoot = True return
def testEnableApiDoneCall_Success(self): """Test EnableService.""" self.ExpectEnableApiCall(self.OPERATION_NAME, done=True) enable_api.EnableService(self.PROJECT_NAME, self.DEFAULT_SERVICE_NAME) self.AssertErrContains("""\ Enabling service [service-name.googleapis.com] on project [fake-project]... """)
def _CheckIamPermissions(project_id): """Check for needed IAM permissions and prompt to add if missing. Args: project_id: A string with the id of the project. """ project = projects_api.Get(project_id) # If the user's project doesn't have cloudbuild enabled yet, then the service # account won't even exist. If so, then ask to enable it before continuing. # Also prompt them to enable Stackdriver Logging if they haven't yet. expected_services = ['cloudbuild.googleapis.com', 'logging.googleapis.com'] for service_name in expected_services: if not services_api.IsServiceEnabled(project.projectId, service_name): # TODO(b/112757283): Split this out into a separate library. prompt_message = ( 'The "{0}" service is not enabled for this project. ' 'It is required for this operation.\n').format(service_name) console_io.PromptContinue( prompt_message, 'Would you like to enable this service?', throw_if_unattended=True, cancel_on_no=True) services_api.EnableService(project.projectId, service_name) # Now that we're sure the service account exists, actually check permissions. policy = projects_api.GetIamPolicy(project_id) build_account = 'serviceAccount:{0}@cloudbuild.gserviceaccount.com'.format( project.projectNumber) _VerifyRolesAndPromptIfMissing( project_id, build_account, _CurrentRolesForAccount(policy, build_account), { 'roles/compute.admin', 'roles/iam.serviceAccountUser', 'roles/iam.serviceAccountTokenCreator' }) # https://cloud.google.com/compute/docs/access/service-accounts#default_service_account compute_account = ( 'serviceAccount:{0}[email protected]'.format( project.projectNumber)) current_compute_account_roles = _CurrentRolesForAccount( policy, compute_account) # By default, the Compute Engine service account has the role `roles/editor` # applied to it, which is sufficient for import and export. If that's not # present, then request the minimal number of permissions. if 'roles/editor' not in current_compute_account_roles: try: _VerifyRolesAndPromptIfMissing( project_id, compute_account, current_compute_account_roles, {'roles/compute.storageAdmin', 'roles/storage.objectViewer'}) except apitools_exceptions.HttpForbiddenError: log.warning( 'Your account does not have permission to add roles to the ' 'default compute engine service account. If import fails, ' 'ensure "{0}" has the roles "{1}" and "{2}" before retrying.'.format( compute_account, 'roles/compute.storageAdmin', 'roles/storage.objectViewer'))
def testEnableApiCall_Success(self): """Test EnableService.""" self.ExpectEnableApiCall(self.OPERATION_NAME) self.ExpectOperation(self.OPERATION_NAME, 0) enable_api.EnableService(self.PROJECT_NAME, self.DEFAULT_SERVICE_NAME) self.AssertErrContains("""\ Enabling service [service-name.googleapis.com] on project [fake-project]... Operation "operations/abc.0000000000" finished successfully. """)
def _CheckIamPermissions(project_id): """Check for needed IAM permissions and prompt to add if missing. Args: project_id: A string with the name of the project. """ project = projects_api.Get(project_id) # If the user's project doesn't have cloudbuild enabled yet, then the service # account won't even exist. If so, then ask to enable it before continuing. # Also prompt them to enable Stackdriver Logging if they haven't yet. expected_services = ['cloudbuild.googleapis.com', 'logging.googleapis.com'] for service_name in expected_services: if not services_api.IsServiceEnabled(project.projectId, service_name): # TODO(b/112757283): Split this out into a separate library. prompt_message = ( 'The "{0}" service is not enabled for this project. ' 'It is required for this operation.\n').format(service_name) console_io.PromptContinue(prompt_message, 'Would you like to enable this service?', throw_if_unattended=True, cancel_on_no=True) services_api.EnableService(project.projectId, service_name) # Now that we're sure the service account exists, actually check permissions. service_account = 'serviceAccount:{0}@cloudbuild.gserviceaccount.com'.format( project.projectNumber) expected_permissions = {'roles/compute.admin': service_account} for role in SERVICE_ACCOUNT_ROLES: expected_permissions[role] = service_account permissions = projects_api.GetIamPolicy(project_id) for binding in permissions.bindings: if expected_permissions.get(binding.role) in binding.members: del expected_permissions[binding.role] if expected_permissions: ep_table = [ '{0} {1}'.format(role, account) for role, account in expected_permissions.items() ] prompt_message = ( 'The following IAM permissions are needed for this operation:\n' '[{0}]\n'.format('\n'.join(ep_table))) console_io.PromptContinue( message=prompt_message, prompt_string='Would you like to add the permissions', throw_if_unattended=True, cancel_on_no=True) for role, account in expected_permissions.items(): log.info('Adding [{0}] to [{1}]'.format(account, role)) projects_api.AddIamPolicyBinding(project_id, account, role)
def Run(self, args): """Default Run method implementation.""" flags.CheckParentFlags(args, parent_required=False) project_id = args.id if not project_id and args.name: candidate = command_lib_util.IdFromName(args.name) if candidate and console_io.PromptContinue( 'No project id provided.', 'Use [{}] as project id'.format(candidate), throw_if_unattended=True): project_id = candidate if not project_id: raise exceptions.RequiredArgumentException( 'PROJECT_ID', 'an id must be provided for the new project') project_ref = command_lib_util.ParseProject(project_id) labels = labels_util.ParseCreateArgs( args, projects_util.GetMessages().Project.LabelsValue) try: create_op = projects_api.Create( project_ref, display_name=args.name, parent=projects_api.ParentNameToResourceId( flags.GetParentFromFlags(args)), labels=labels) except apitools_exceptions.HttpConflictError: msg = ( 'Project creation failed. The project ID you specified is ' 'already in use by another project. Please try an alternative ' 'ID.') core_exceptions.reraise(exceptions.HttpException(msg)) log.CreatedResource(project_ref, is_async=True) create_op = operations.WaitForOperation(create_op) # Enable cloudapis.googleapis.com if args.enable_cloud_apis: log.debug('Enabling cloudapis.googleapis.com') enable_api.EnableService(project_ref.Name(), 'cloudapis.googleapis.com') if args.set_as_default: project_property = properties.FromString('core/project') properties.PersistProperty(project_property, project_id) log.status.Print( 'Updated property [core/project] to [{0}].'.format(project_id)) return operations.ExtractOperationResponse( create_op, apis.GetMessagesModule('cloudresourcemanager', 'v1').Project)
def CheckForAssetInventoryEnablementWithPrompt(project=None): """Checks if the cloudasset API is enabled, prompts to enable if not.""" project = project or properties.VALUES.core.project.GetOrFail() service_name = 'cloudasset.googleapis.com' if not enable_api.IsServiceEnabled(project, service_name): if console_io.PromptContinue( default=False, prompt_string=( 'API [{}] is required to continue, but is not enabled on project [{}]. ' 'Would you like to enable and retry (this will take a ' 'few minutes)?').format(service_name, project)): enable_api.EnableService(project, service_name) else: raise AssetInventoryNotEnabledException( 'Aborted by user: API [{}] must be enabled on project [{}] to continue.' .format(service_name, project))
def AttemptToEnableService(self, service_name, is_async): """Attempt to enable a service. If lacking permission, log a warning. Args: service_name: The service to enable. is_async: If true, return immediately instead of waiting for the operation to finish. """ project_id = properties.VALUES.core.project.Get(required=True) try: # Enable the produced service. enable_api.EnableService(project_id, service_name, is_async) except services_exceptions.EnableServicePermissionDeniedException: log.warning(('Attempted to enable service [{0}] on project [{1}], but ' 'did not have required permissions. Please ensure this ' 'service is enabled before using your Endpoints ' 'service.').format(service_name, project_id))
def PromptToEnableApi(project, service_token): """Prompts to enable the API. Args: project (str): The project that the API is not enabled on. service_token (str): The service token of the API to prompt for. Returns: bool, whether or not the API was attempted to be enabled """ api_enable_attempted = console_io.PromptContinue( default=False, prompt_string=('API [{}] not enabled on project [{}]. ' 'Would you like to enable and retry (this will take a ' 'few minutes)?').format(service_token, project)) if api_enable_attempted: enable_api.EnableService(project, service_token) return api_enable_attempted
def PromptToEnableApi(service_name): """Prompts to enable the API and throws if the answer is no. Args: service_name: str, The service token of the API to prompt for. """ if not properties.VALUES.core.should_prompt_to_enable_api.GetBool(): return project = properties.VALUES.core.project.Get(required=True) # Don't prompt to enable an already enabled API if not enable_api.IsServiceEnabled(project, service_name): if console_io.PromptContinue( default=False, cancel_on_no=True, prompt_string=('API [{}] not enabled on project [{}]. ' 'Would you like to enable and retry (this will take a ' 'few minutes)?').format(service_name, project)): enable_api.EnableService(project, service_name)
def check_prerequisite(self): log.status.Print('---- Checking VM status ----') msg = 'The Monitoring API is needed to check the VM\'s Status.' prompt = 'Is it OK to enable it and check the VM\'s Status?' cancel = 'Test skipped.' try: prompt_continue = console_io.PromptContinue(message=msg, prompt_string=prompt, cancel_on_no=True, cancel_string=cancel) self.skip_troubleshoot = not prompt_continue except OperationCancelledError: self.skip_troubleshoot = True if self.skip_troubleshoot: return # Enable API enable_api.EnableService(self.project.name, MONITORING_API)
def _CheckIamPermissions(project_id, cloudbuild_service_account_roles, compute_service_account_roles, custom_compute_service_account=''): """Check for needed IAM permissions and prompt to add if missing. Args: project_id: A string with the id of the project. cloudbuild_service_account_roles: A set of roles required for cloudbuild service account. compute_service_account_roles: A set of roles required for compute service account. custom_compute_service_account: Custom compute service account """ project = projects_api.Get(project_id) # If the user's project doesn't have cloudbuild enabled yet, then the service # account won't even exist. If so, then ask to enable it before continuing. # Also prompt them to enable Cloud Logging if they haven't yet. expected_services = ['cloudbuild.googleapis.com', 'logging.googleapis.com', 'compute.googleapis.com'] for service_name in expected_services: if not services_api.IsServiceEnabled(project.projectId, service_name): # TODO(b/112757283): Split this out into a separate library. prompt_message = ( 'The "{0}" service is not enabled for this project. ' 'It is required for this operation.\n').format(service_name) enable_service = console_io.PromptContinue( prompt_message, 'Would you like to enable this service?', throw_if_unattended=True) if enable_service: services_api.EnableService(project.projectId, service_name) else: log.warning( 'If import fails, manually enable {0} before retrying. For ' 'instructions on enabling services, see ' 'https://cloud.google.com/service-usage/docs/enable-disable.' .format(service_name)) build_account = 'serviceAccount:{0}@cloudbuild.gserviceaccount.com'.format( project.projectNumber) # https://cloud.google.com/compute/docs/access/service-accounts#default_service_account compute_account = ( 'serviceAccount:{0}[email protected]'.format( project.projectNumber)) if custom_compute_service_account: compute_account = 'serviceAccount:{0}'.format( custom_compute_service_account) # Now that we're sure the service account exists, actually check permissions. try: policy = projects_api.GetIamPolicy(project_id) except apitools_exceptions.HttpForbiddenError: log.warning( 'Your account does not have permission to check roles for the ' 'service account {0}. If import fails, ' 'ensure "{0}" has the roles "{1}" and "{2}" has the roles "{3}" before ' 'retrying.'.format(build_account, cloudbuild_service_account_roles, compute_account, compute_service_account_roles)) return _VerifyRolesAndPromptIfMissing(project_id, build_account, _CurrentRolesForAccount(policy, build_account), frozenset(cloudbuild_service_account_roles)) current_compute_account_roles = _CurrentRolesForAccount( policy, compute_account) # By default, the Compute Engine service account has the role `roles/editor` # applied to it, which is sufficient for import and export. If that's not # present, then request the minimal number of permissions. if ROLE_EDITOR not in current_compute_account_roles: _VerifyRolesAndPromptIfMissing( project_id, compute_account, current_compute_account_roles, compute_service_account_roles)