def Run(self, args): """Run the authentication command.""" if c_devshell.IsDevshellEnvironment(): message = """ You are already authenticated with gcloud when running inside the Developer Shell and so do not need to run this command. Do you wish to proceed anyway? """ answer = console_io.PromptContinue(message=message) if not answer: return None if c_gce.Metadata().connected: message = textwrap.dedent(""" You are running on a GCE VM. It is recommended that you use service accounts for authentication. You can run: $ gcloud config set account ``ACCOUNT'' to switch accounts if necessary. Your credentials may be visible to others with access to this virtual machine. Are you sure you want to authenticate with your personal account? """) answer = console_io.PromptContinue(message=message) if not answer: return None account = args.account if account and not args.force: creds = c_store.LoadIfValid(account=account) if creds: # Account already has valid creds, just switch to it. return self.LoginAs(account, creds, args.project, args.do_not_activate) # No valid creds, do the web flow. creds = self.DoWebFlow(args.launch_browser) web_flow_account = creds.id_token['email'] if account and account.lower() != web_flow_account.lower(): raise c_exc.ToolException( 'You attempted to log in as account [{account}] but the received ' 'credentials were for account [{web_flow_account}].\n\n' 'Please check that your browser is logged in as account [{account}] ' 'and that you are using the correct browser profile.'.format( account=account, web_flow_account=web_flow_account)) account = web_flow_account # We got new creds, and they are for the correct user. c_store.Store(creds, account) return self.LoginAs(account, creds, args.project, args.do_not_activate)
def Run(self, args): """This is what gets called when the user runs this command. Args: args: an argparse namespace, All the arguments that were provided to this command invocation. Raises: HttpException: An http error response was received while executing api request. Returns: None """ prompt_message = ( 'Deleting dataset {0} will delete all objects in the dataset. ' 'Deleted datasets can be recovered with the "restore" command ' 'up to one week after the deletion occurs.').format(args.id) if not console_io.PromptContinue(message=prompt_message): raise GenomicsError('Deletion aborted by user.') apitools_client = self.context[commands.GENOMICS_APITOOLS_CLIENT_KEY] genomics_messages = self.context[commands.GENOMICS_MESSAGES_MODULE_KEY] dataset = genomics_messages.GenomicsDatasetsDeleteRequest( datasetId=str(args.id), ) apitools_client.datasets.Delete(dataset) log.Print('Deleted dataset {0}'.format(args.id))
def GetPublicKey(self): """Generates an SSH key using ssh-key (if necessary) and returns it.""" public_ssh_key_file = self.ssh_key_file + '.pub' if (not os.path.exists(self.ssh_key_file) or not os.path.exists(public_ssh_key_file)): log.warn('You do not have an SSH key for Google Compute Engine.') log.warn('[%s] will be executed to generate a key.', self.ssh_keygen_executable) ssh_directory = os.path.dirname(public_ssh_key_file) if not os.path.exists(ssh_directory): if console_io.PromptContinue( 'This tool needs to create the directory [{0}] before being able ' 'to generate SSH keys.'.format(ssh_directory)): files.MakeDir(ssh_directory, 0700) else: raise exceptions.ToolException('SSH key generation aborted by user.') keygen_args = [ self.ssh_keygen_executable, '-t', 'rsa', '-f', self.ssh_key_file, ] _RunExecutable(keygen_args) with open(public_ssh_key_file) as f: return f.readline().strip()
def EnsureSSHKeyExistsForUser(self, user): """Ensure the user's public SSH key is known by the Account Service.""" public_key = self.GetPublicKey() should_upload = True try: user_info = self.LookupUser(user) except user_utils.UserException: owner_email = gaia_utils.GetAuthenticatedGaiaEmail(self.http) if console_io.PromptContinue( 'The user [{0}] does not exist. Would you like to create a new ' 'Compute Accounts user owned by [{1}]?'.format( user, owner_email)): self.CreateUser(user, owner_email) user_info = self.LookupUser(user) else: raise exceptions.ToolException( 'user creation aborted by user.') for remote_public_key in user_info.publicKeys: if remote_public_key.key.rstrip() == public_key: expiration_time = remote_public_key.expirationTimestamp if expiration_time and time_utils.IsExpired(expiration_time): # If a key is expired we remove and reupload self.RemovePublicKey(user_info.name, remote_public_key.fingerprint) else: should_upload = False break if should_upload: self.UploadPublicKey(user, public_key) return True
def Run(self, args): """Run 'dns managed-zone delete'. Args: args: argparse.Namespace, The arguments that this command was invoked with. Returns: A dict object representing the changes resource obtained by the delete operation if the delete was successful. """ project = properties.VALUES.core.project.Get(required=True) really = console_io.PromptContinue( 'Deleting %s in %s' % (args.zone, project)) if not really: return dns = self.context['dns'] if args.delete_zone_contents: self.DeleteZoneContents_(dns, project, args.zone) request = dns.managedZones().delete(project=project, managedZone=args.zone) try: result = request.execute() return result except errors.HttpError as error: raise exceptions.HttpException(util.GetError(error, verbose=True)) except errors.Error as error: raise exceptions.ToolException(error)
def Run(self, args): """Run 'dns managed-zone create'. Args: args: argparse.Namespace, The arguments that this command was invoked with. Returns: A dict object representing the changes resource obtained by the create operation if the create was successful. """ project = properties.VALUES.core.project.Get(required=True) zone = {} zone['dnsName'] = args.dns_name zone['name'] = args.zone zone['description'] = args.description really = console_io.PromptContinue('Creating %s in %s' % (zone, project)) if not really: return dns = self.context['dns'] request = dns.managedZones().create(project=project, body=zone) try: result = request.execute() return result except errors.HttpError as error: raise exceptions.HttpException(util.GetError(error)) except errors.Error as error: raise exceptions.ToolException(error)
def Prompts(usage_reporting): """Display prompts to opt out of usage reporting. Args: usage_reporting: bool, If True, enable usage reporting. If None, ask. """ if config.InstallationConfig.Load().IsAlternateReleaseChannel(): usage_reporting = True print(""" Usage reporting is always on for alternate release channels. """) return if usage_reporting is None: print(""" The Google Cloud SDK is currently in developer preview. To help improve the quality of this product, we collect anonymized data on how the SDK is used. You may choose to opt out of this collection now (by choosing 'N' at the below prompt), or at any time in the future by running the following command: gcloud config set --scope=user disable_usage_reporting true """) usage_reporting = console_io.PromptContinue( prompt_string='Do you want to help improve the Google Cloud SDK') properties.PersistProperty(properties.VALUES.core.disable_usage_reporting, not usage_reporting, scope=properties.Scope.INSTALLATION)
def Run(self, args): """Deletes a Cloud SQL instance. Args: args: argparse.Namespace, The arguments that this command was invoked with. Returns: A dict object representing the operations resource describing the delete operation if the delete was successful. Raises: HttpException: A http error response was received while executing api request. ToolException: An error other than http error occured while executing the command. """ sql_client = self.context['sql_client'] sql_messages = self.context['sql_messages'] resources = self.context['registry'] operation_ref = None util.ValidateInstanceName(args.instance) instance_ref = resources.Parse(args.instance, collection='sql.instances') if not console_io.PromptContinue( 'All of the instance data will be lost when the instance is deleted.' ): return None try: result = sql_client.instances.Delete( sql_messages.SqlInstancesDeleteRequest( instance=instance_ref.instance, project=instance_ref.project)) operation_ref = resources.Create( 'sql.operations', operation=result.operation, project=instance_ref.project, instance=instance_ref.instance, ) unused_operation = sql_client.operations.Get( operation_ref.Request()) log.DeletedResource(instance_ref) cache = remote_completion.RemoteCompletion() cache.DeleteFromCache(instance_ref.SelfLink()) except apitools_base.HttpError: log.debug('operation : %s', str(operation_ref)) raise
def Run(self, args): log.status.write('Welcome! This command will take you through ' 'the configuration of gcloud.\n\n') creds = c_store.LoadIfValid() if not creds: log.status.write('To start, you must login. When the web browser opens, ' 'login with your google account and hit accept.\n\n') answer = console_io.PromptContinue() if not answer: return creds = self.cli.Execute(['auth', 'login']) log.status.write('\nYou are now logged in as: [{0}]\n' .format(creds.id_token['email']))
def Run(self, args): """This is what gets called when the user runs this command. Args: args: an argparse namespace, All the arguments that were provided to this command invocation. Raises: ToolException: when user cancels dataset removal. Returns: Some value that we want to have printed later. """ apitools_client = self.context[commands.APITOOLS_CLIENT_KEY] bigquery_messages = self.context[commands.BIGQUERY_MESSAGES_MODULE_KEY] resource_parser = self.context[commands.BIGQUERY_REGISTRY_KEY] resource = resource_parser.Parse(args.dataset_name, collection='bigquery.datasets') reference = message_conversions.DatasetResourceToReference( bigquery_messages, resource) if not args.quiet: dataset_exists = bigquery_client_helper.DatasetExists( apitools_client, bigquery_messages, reference) if dataset_exists: removal_confirmed = console_io.PromptContinue( message='About to remove dataset {0}.'.format(resource)) if not removal_confirmed: raise exceptions.ToolException('canceled by user') request = bigquery_messages.BigqueryDatasetsDeleteRequest( projectId=reference.projectId, datasetId=reference.datasetId, deleteContents=args.remove_tables) try: apitools_client.datasets.Delete(request) log.DeletedResource(resource) except apitools_base.HttpError as server_error: try: raise bigquery.Error.ForHttpError(server_error) except bigquery.NotFoundError: if args.ignore_not_found: log.status.Print( 'Dataset {0} did not exist.'.format(resource)) else: raise
def PromptIfDisksWithoutAutoDeleteWillBeDeleted(self, args, disks_to_warn_for): """Prompts if disks with False autoDelete will be deleted.""" if not disks_to_warn_for: return prompt_list = [] disk_refs = self.CreateZonalReferences(disks_to_warn_for, args.zone, resource_type='disks') for ref in disk_refs: prompt_list.append('[{0}] in [{1}]'.format(ref.Name(), ref.zone)) prompt_message = utils.ConstructList( 'The following disks are not configured to be automatically deleted ' 'with instance deletion, but they will be deleted as a result of ' 'this operation if they are not attached to any other instances:', prompt_list) if not console_io.PromptContinue(message=prompt_message): raise exceptions.ToolException('Deletion aborted by user.')
def _PromptDidYouMeanScope(self, ambiguous_refs, attribute, resource_type, suggested_resource, raise_on_prompt_failure): """Prompts "did you mean <scope>". Returns str or None, or raises.""" # targetInstances -> target instances resource_name = utils.CamelCaseToOutputFriendly(resource_type) names = ['[{0}]'.format(name) for name, _ in ambiguous_refs] message = 'Did you mean {0} [{1}] for {2}: [{3}]?'.format( attribute, suggested_resource, resource_name, names) try: if console_io.PromptContinue(message=message, default=True, throw_if_unattended=True): return suggested_resource else: return None except console_io.UnattendedPromptError: raise_on_prompt_failure()
def PrintAndConfirmWarningMessage(self, args): """Print and confirm warning indicating the effect of applying the patch.""" continue_msg = None if any([args.tier, args.database_flags, args.clear_database_flags, args.enable_database_replication, args.no_enable_database_replication]): continue_msg = ('WARNING: This patch modifies a value that requires ' 'your instance to be restarted. Submitting this patch ' 'will immediately restart your instance if it\'s running.' ) else: if any([args.follow_gae_app, args.gce_zone]): continue_msg = ('WARNING: This patch modifies the zone your instance ' 'is set to run in, which may require it to be moved. ' 'Submitting this patch will restart your instance ' 'if it is running in a different zone.') if continue_msg and not console_io.PromptContinue(continue_msg): raise exceptions.ToolException('canceled by the user.')
def PromptForDeletion(refs, scope_name=None, prompt_title=None): """Prompts the user to confirm deletion of resources.""" if not refs: return resource_type = CollectionToResourceType(refs[0].Collection()) resource_name = CamelCaseToOutputFriendly(resource_type) prompt_list = [] for ref in refs: if scope_name: item = '[{0}] in [{1}]'.format(ref.Name(), getattr(ref, scope_name)) else: item = '[{0}]'.format(ref.Name()) prompt_list.append(item) prompt_title = (prompt_title or 'The following {0} will be deleted:'.format(resource_name)) prompt_message = ConstructList(prompt_title, prompt_list) if not console_io.PromptContinue(message=prompt_message): raise calliope_exceptions.ToolException('Deletion aborted by user.')
def Restore(self): """Restores the latest backup installation of the Cloud SDK. Raises: NoBackupError: If there is no valid backup to restore. """ self._EnsureNotDisabled() install_state = self._GetInstallState() if not install_state.HasBackup(): raise NoBackupError('There is currently no backup to restore.') self._ShouldDoFastUpdate(allow_no_backup=False, fast_mode_impossible=True) if not console_io.PromptContinue( message='Your Cloud SDK installation will be restored to its previous ' 'state.'): return self.__Write(log.status, 'Restoring backup...') install_state.RestoreBackup() self.__Write(log.status, 'Restoration done!\n')
def Restore(self): """Restores the latest backup installation of the Cloud SDK. Raises: NoBackupError: If there is no valid backup to restore. """ self._EnsureNotDisabled() install_state = self._GetInstallState() if not install_state.HasBackup(): raise NoBackupError('There is currently no backup to restore.') self._CheckCWD() if not console_io.PromptContinue( message= 'Your Cloud SDK installation will be restored to its previous ' 'state.'): return self.__Write('Restoring backup...\n') install_state.RestoreBackup() self.__Write('\nDone!\n')
def Run(self, args): """This is what gets called when the user runs this command. Args: args: an argparse namespace, All the arguments that were provided to this command invocation. Raises: ToolException: if user cancels table removal. """ apitools_client = self.context[commands.APITOOLS_CLIENT_KEY] bigquery_messages = self.context[commands.BIGQUERY_MESSAGES_MODULE_KEY] resource_parser = self.context[commands.BIGQUERY_REGISTRY_KEY] table_reference = resource_parser.Parse( args.table_or_view, collection='bigquery.tables') if not args.quiet: if not console_io.PromptContinue( message='About to delete table [{0}].'.format(table_reference)): raise calliope_exceptions.ToolException('canceled by user') request = bigquery_messages.BigqueryTablesDeleteRequest( projectId=table_reference.projectId, datasetId=table_reference.datasetId, tableId=table_reference.tableId) try: apitools_client.tables.Delete(request) except apitools_base.HttpError as server_error: try: raise bigquery.Error.ForHttpError(server_error) except bigquery.NotFoundError: if args.ignore_not_found: log.status.Print('Table [{0}] did not exist.'.format(table_reference)) else: raise
def Run(self, args): """This is what gets called when the user runs this command. Args: args: an argparse namespace. All the arguments that were provided to this command invocation. Returns: Some value that we want to have printed later. """ adapter = self.context['api_adapter'] cluster_refs = [] for name in args.names: cluster_refs.append(adapter.ParseCluster(name)) if not console_io.PromptContinue(message=util.ConstructList( 'The following clusters will be deleted.', [ '[{name}] in [{zone}]'.format(name=ref.clusterId, zone=adapter.Zone(ref)) for ref in cluster_refs ]), throw_if_unattended=True): raise exceptions.ToolException('Deletion aborted by user.') operations = [] errors = [] # Issue all deletes first for cluster_ref in cluster_refs: try: # Make sure it exists (will raise appropriate error if not) adapter.GetCluster(cluster_ref) op_ref = adapter.DeleteCluster(cluster_ref) operations.append((op_ref, cluster_ref)) except apitools_base.HttpError as error: errors.append(util.GetError(error)) except util.Error as error: errors.append(error) if args.wait: # Poll each operation for completion for operation_ref, cluster_ref in operations: try: adapter.WaitForOperation( operation_ref, 'Deleting cluster {0}'.format(cluster_ref.clusterId)) # Purge cached config files util.ClusterConfig.Purge(cluster_ref.clusterId, adapter.Zone(cluster_ref), cluster_ref.projectId) if properties.VALUES.container.cluster.Get( ) == cluster_ref.clusterId: properties.PersistProperty( properties.VALUES.container.cluster, None) log.DeletedResource(cluster_ref) except apitools_base.HttpError as error: errors.append(util.GetError(error)) except util.Error as error: errors.append(error) if errors: raise exceptions.ToolException( util.ConstructList('Some requests did not succeed:', errors))
def Run(self, args): start = time_utils.CurrentTimeSec() # Set up Encryption utilities. openssl_executable = files.FindExecutableOnPath('openssl') if openssl_executable: crypt = openssl_encryption_utils.OpensslCrypt(openssl_executable) elif windows_encryption_utils: crypt = windows_encryption_utils.WinCrypt() else: raise exceptions.ToolException( 'Your platform does not support OpenSSL.') # Get Authenticated email address and default username. email = gaia_utils.GetAuthenticatedGaiaEmail(self.http) if args.user: user = args.user else: user = gaia_utils.MapGaiaEmailToDefaultAccountName(email) # Warn user (This warning doesn't show for non-interactive sessions). message = RESET_PASSWORD_WARNING.format(user) prompt_string = ( 'Would you like to set or reset the password for [{0}]'.format( user)) console_io.PromptContinue(message=message, prompt_string=prompt_string, cancel_on_no=True) log.status.Print( 'Resetting and retrieving password for [{0}] on [{1}]'.format( user, args.instance)) # Get Encryption Keys. key = crypt.GetKeyPair() modulus, exponent = crypt.GetModulusExponentFromPublicKey( crypt.GetPublicKey(key)) # Create Windows key entry. self.windows_key_entry = self._ConstructWindowsKeyEntry( user, modulus, exponent, email) # Call ReadWriteCommad.Run() which will fetch the instance and update # the metadata (using the data in self.windows_key_entry). objects = super(ResetWindowsPassword, self).Run(args) updated_instance = list(objects)[0] # Retrieve and Decrypt the password from the serial console. enc_password = self._GetEncryptedPasswordFromSerialPort(modulus) password = crypt.DecryptMessage(key, enc_password) # Get External IP address. try: access_configs = updated_instance['networkInterfaces'][0][ 'accessConfigs'] external_ip_address = access_configs[0]['natIP'] except KeyError: log.warn(NO_IP_WARNING.format(updated_instance['name'])) external_ip_address = None # Check for old Windows credentials. if self.old_metadata_keys: log.warn( OLD_KEYS_WARNING.format(self.ref.Name(), self.ref.Name(), self.ref.zone, ' '.join(self.old_metadata_keys))) log.info('Total Elapsed Time: {0}'.format(time_utils.CurrentTimeSec() - start)) # Display the connection info. connection_info = { 'username': user, 'password': password, 'ip_address': external_ip_address } resource_printer.Print(connection_info, args.format or 'text', out=None)
def Run(self, args): self.ref = self.CreateReference(args) get_request = self.GetGetRequest(args) errors = [] objects = list(request_helper.MakeRequests( requests=[get_request], http=self.http, batch_url=self.batch_url, errors=errors, custom_get_requests=None)) if errors: utils.RaiseToolException( errors, error_message='Could not fetch resource:') self.original_object = objects[0] self.original_record = encoding.MessageToDict(self.original_object) # Selects only the fields that can be modified. field_selector = property_selector.PropertySelector( properties=self._resource_spec.editables) self.modifiable_record = field_selector.Apply(self.original_record) buf = cStringIO.StringIO() for line in _HELP.splitlines(): buf.write('#') if line: buf.write(' ') buf.write(line) buf.write('\n') buf.write('\n') buf.write(_SerializeDict(self.modifiable_record, args.format or BaseEdit.DEFAULT_FORMAT)) buf.write('\n') example = _SerializeDict( encoding.MessageToDict(self.example_resource), args.format or BaseEdit.DEFAULT_FORMAT) _WriteResourceInCommentBlock(example, 'Example resource:', buf) buf.write('#\n') original = _SerializeDict(self.original_record, args.format or BaseEdit.DEFAULT_FORMAT) _WriteResourceInCommentBlock(original, 'Original resource:', buf) file_contents = buf.getvalue() while True: file_contents = edit.OnlineEdit(file_contents) try: resources = self.ProcessEditedResource(file_contents, args) break except (ValueError, yaml.error.YAMLError, protorpc.messages.ValidationError, calliope_exceptions.ToolException) as e: if isinstance(e, ValueError): message = e.message else: message = str(e) if isinstance(e, calliope_exceptions.ToolException): problem_type = 'applying' else: problem_type = 'parsing' message = ('There was a problem {0} your changes: {1}' .format(problem_type, message)) if not console_io.PromptContinue( message=message, prompt_string='Would you like to edit the resource again?'): raise calliope_exceptions.ToolException('Edit aborted by user.') resources = lister.ProcessResults( resources=resources, field_selector=property_selector.PropertySelector( properties=None, transformations=self.transformations)) for resource in resources: yield resource
def Run(self, args): """This is what gets called when the user runs this command. Args: args: an argparse namespace. All the arguments that were provided to this command invocation. Returns: Some value that we want to have printed later. """ client = self.context['container_client'] messages = self.context['container_messages'] resources = self.context['registry'] properties.VALUES.compute.zone.Get(required=True) properties.VALUES.core.project.Get(required=True) cluster_refs = [] for name in args.names: cluster_refs.append( resources.Parse( name, collection='container.projects.zones.clusters')) if not console_io.PromptContinue(message=util.ConstructList( 'The following clusters will be deleted.', [ '[{name}] in [{zone}]'.format(name=ref.clusterId, zone=ref.zoneId) for ref in cluster_refs ]), throw_if_unattended=True): raise exceptions.ToolException('Deletion aborted by user.') operations = [] errors = [] # Issue all deletes first for ref in cluster_refs: try: # Make sure it exists (will raise appropriate error if not) util.DescribeCluster(ref, self.context) op = client.projects_zones_clusters.Delete( messages.ContainerProjectsZonesClustersDeleteRequest( clusterId=ref.clusterId, zoneId=ref.zoneId, projectId=ref.projectId)) operations.append((op, ref)) except apitools_base.HttpError as error: errors.append(util.GetError(error)) except util.Error as error: errors.append(error) if args.wait: # Poll each operation for completion for operation, ref in operations: try: util.WaitForOperation( operation, ref.projectId, self.context, 'Deleting cluster {0}'.format(ref.clusterId)) # Purge cached config files util.ClusterConfig.Purge(ref.clusterId, ref.zoneId, ref.projectId) if properties.VALUES.container.cluster.Get( ) == ref.clusterId: properties.PersistProperty( properties.VALUES.container.cluster, None) log.DeletedResource(ref) except apitools_base.HttpError as error: errors.append(util.GetError(error)) except util.Error as error: errors.append(error) if errors: raise exceptions.ToolException( util.ConstructList('Some requests did not succeed:', errors))
def _DoFreshInstall(self, message, no_update, download_url): """Do a reinstall of what we have based on a fresh download of the SDK. Args: message: str, A message to show to the user before the re-installation. no_update: bool, True to show the message and tell the user they must re-download manually. download_url: The URL the Cloud SDK can be downloaded from. Returns: bool, True if the update succeeded, False if it was cancelled. """ self._EnsureNotDisabled() if os.environ.get('CLOUDSDK_REINSTALL_COMPONENTS'): # We are already reinstalling but got here somehow. Something is very # wrong and we want to avoid the infinite loop. self._RaiseReinstallationFailedError() # Print out an arbitrary message that we wanted to show users for this # update. if message: self.__Write(log.status, msg=message, word_wrap=True) # We can decide that for some reason we just never want to update past this # version of the schema. if no_update: return False answer = console_io.PromptContinue( message= '\nThe component manager must perform a self update before you ' 'can continue. It and all components will be updated to their ' 'latest versions.') if not answer: return False self._ShouldDoFastUpdate(allow_no_backup=False, fast_mode_impossible=True) install_state = self._GetInstallState() try: with console_io.ProgressBar( label='Downloading and extracting updated components', stream=log.status) as pb: staging_state = install_state.CreateStagingFromDownload( download_url, progress_callback=pb.SetProgress) except local_state.Error: log.error('An updated Cloud SDK failed to download') log.debug('Handling re-installation error', exc_info=True) self._RaiseReinstallationFailedError() # shell out to install script installed_component_ids = sorted( install_state.InstalledComponents().keys()) env = dict(os.environ) env['CLOUDSDK_REINSTALL_COMPONENTS'] = ','.join( installed_component_ids) installer_path = os.path.join(staging_state.sdk_root, 'bin', 'bootstrapping', 'install.py') p = subprocess.Popen([sys.executable, '-S', installer_path], env=env) ret_val = p.wait() if ret_val: self._RaiseReinstallationFailedError() self.__Write(log.status, 'Creating backup and activating new installation...') install_state.ReplaceWith(staging_state) self.__Write(log.status, '\nComponents updated!\n') return True
def _DoFreshInstall(self, e): """Do a reinstall of what we have based on a fresh download of the SDK. Args: e: snapshots.IncompatibleSchemaVersionError, The exception we got with information about the new schema version. """ self._EnsureNotDisabled() if os.environ.get('CLOUDSDK_REINSTALL_COMPONENTS'): # We are already reinstalling but got here somehow. Something is very # wrong and we want to avoid the infinite loop. self._RaiseReinstallationFailedError() # Print out an arbitrary message that we wanted to show users for this # udpate. message = e.schema_version.message if message: self.__Write(msg=message, word_wrap=True) # We can decide that for some reason we just never want to update past this # version of the schema. if e.schema_version.no_update: return answer = console_io.PromptContinue( message= '\nThe component manager must perform a self update before you ' 'can continue. It and all components will be updated to their ' 'latest versions.') if not answer: return self._CheckCWD() install_state = self._GetInstallState() self.__Write('Downloading and extracting updated components...\n') download_url = e.schema_version.url try: staging_state = install_state.CreateStagingFromDownload( download_url) except local_state.Error: log.error('An updated Cloud SDK failed to download') log.debug('Handling re-installation error', exc_info=True) self._RaiseReinstallationFailedError() # shell out to install script installed_component_ids = sorted( install_state.InstalledComponents().keys()) env = dict(os.environ) env['CLOUDSDK_REINSTALL_COMPONENTS'] = ','.join( installed_component_ids) installer_path = os.path.join(staging_state.sdk_root, 'bin', 'bootstrapping', 'install.py') p = subprocess.Popen([sys.executable, '-S', installer_path], env=env) ret_val = p.wait() if ret_val: self._RaiseReinstallationFailedError() self.__Write('Creating backup and activating new installation...') install_state.ReplaceWith(staging_state) self.__Write('\nDone!\n')
def Remove(self, ids, allow_no_backup=False): """Uninstalls the given components. Args: ids: list of str, The component ids to uninstall. allow_no_backup: bool, True if we want to allow the updater to run without creating a backup. This lets us be in the root directory of the SDK and still do an update. It is more fragile if there is a failure, so we only do it if necessary. Raises: InvalidComponentError: If any of the given component ids are not installed or cannot be removed. """ self._EnsureNotDisabled() if not ids: return install_state = self._GetInstallState() snapshot = install_state.Snapshot() id_set = set(ids) not_installed = id_set - set(snapshot.components.keys()) if not_installed: raise InvalidComponentError( 'The following components are not currently installed [{components}]' .format(components=', '.join(not_installed))) required_components = set( c_id for c_id, component in snapshot.components.iteritems() if c_id in id_set and component.is_required) if required_components: raise InvalidComponentError( ('The following components are required and cannot be removed ' '[{components}]').format( components=', '.join(required_components))) to_remove = snapshot.ConsumerClosureForComponents(ids) if not to_remove: self.__Write(log.status, 'No components to remove.\n') return disable_backup = self._ShouldDoFastUpdate( allow_no_backup=allow_no_backup) components_to_remove = sorted(snapshot.ComponentsFromIds(to_remove), key=lambda c: c.details.display_name) self._PrintPendingAction(components_to_remove, 'removed') self.__Write(log.status) message = self._GetDontCancelMessage(disable_backup) if not console_io.PromptContinue(message): return if disable_backup: with execution_utils.UninterruptibleSection(): self.__Write(log.status, 'Performing in place update...\n') self._UpdateWithProgressBar(components_to_remove, 'Uninstalling', install_state.Uninstall) else: with console_io.ProgressBar(label='Creating update staging area', stream=log.status) as pb: staging_state = install_state.CloneToStaging(pb.SetProgress) self.__Write(log.status) self._UpdateWithProgressBar(components_to_remove, 'Uninstalling', staging_state.Uninstall) self.__Write(log.status) self.__Write(log.status, 'Creating backup and activating new installation...') install_state.ReplaceWith(staging_state) self.__Write(log.status, '\nUninstall done!\n')
def Update(self, update_seed=None, allow_no_backup=False, throw_if_unattended=False): """Performs an update of the given components. If no components are provided, it will attempt to update everything you have installed. Args: update_seed: list of str, A list of component ids to update. allow_no_backup: bool, True if we want to allow the updater to run without creating a backup. This lets us be in the root directory of the SDK and still do an update. It is more fragile if there is a failure, so we only do it if necessary. throw_if_unattended: bool, True to throw an exception on prompts when not running in interactive mode. Returns: bool, True if the update succeeded (or there was nothing to do, False if if was cancelled by the user. Raises: InvalidComponentError: If any of the given component ids do not exist. """ self._EnsureNotDisabled() try: install_state, diff = self._GetStateAndDiff() except snapshots.IncompatibleSchemaVersionError as e: return self._ReinstallOnError(e) if update_seed: invalid_seeds = diff.InvalidUpdateSeeds(update_seed) if invalid_seeds: if os.environ.get('CLOUDSDK_REINSTALL_COMPONENTS'): # We are doing a reinstall. Ignore any components that no longer # exist. update_seed = set(update_seed) - invalid_seeds else: raise InvalidComponentError( 'The following components are unknown [{invalid_seeds}]' .format(invalid_seeds=', '.join(invalid_seeds))) else: update_seed = diff.current.components.keys() to_remove = diff.ToRemove(update_seed) to_install = diff.ToInstall(update_seed) self.__Write(log.status) if not to_remove and not to_install: self.__Write(log.status, 'All components are up to date.') with install_state.LastUpdateCheck() as update_check: update_check.SetFromSnapshot(diff.latest, force=True) return True disable_backup = self._ShouldDoFastUpdate( allow_no_backup=allow_no_backup) self._PrintPendingAction( diff.DetailsForCurrent(to_remove - to_install), 'removed') self._PrintPendingAction(diff.DetailsForLatest(to_remove & to_install), 'updated') self._PrintPendingAction(diff.DetailsForLatest(to_install - to_remove), 'installed') self.__Write(log.status) message = self._GetDontCancelMessage(disable_backup) if not console_io.PromptContinue( message=message, throw_if_unattended=throw_if_unattended): return False components_to_install = diff.DetailsForLatest(to_install) components_to_remove = diff.DetailsForCurrent(to_remove) for c in components_to_install: metrics.Installs(c.id, c.version.version_string) if disable_backup: with execution_utils.UninterruptibleSection(): self.__Write(log.status, 'Performing in place update...\n') self._UpdateWithProgressBar(components_to_remove, 'Uninstalling', install_state.Uninstall) self._UpdateWithProgressBar( components_to_install, 'Installing', self._InstallFunction(install_state, diff)) else: with console_io.ProgressBar(label='Creating update staging area', stream=log.status) as pb: staging_state = install_state.CloneToStaging(pb.SetProgress) self.__Write(log.status) self._UpdateWithProgressBar(components_to_remove, 'Uninstalling', staging_state.Uninstall) self._UpdateWithProgressBar( components_to_install, 'Installing', self._InstallFunction(staging_state, diff)) self.__Write(log.status) self.__Write(log.status, 'Creating backup and activating new installation...') install_state.ReplaceWith(staging_state) with install_state.LastUpdateCheck() as update_check: update_check.SetFromSnapshot(diff.latest, force=True) self.__Write(log.status, '\nUpdate done!\n') if self.__warn: bad_commands = self.FindAllOldToolsOnPath() if bad_commands and not os.environ.get( 'CLOUDSDK_REINSTALL_COMPONENTS'): log.warning("""\ There are older versions of Google Cloud Platform tools on your system PATH. Please remove the following to avoid accidentally invoking these old tools: {0} """.format('\n'.join(bad_commands))) return True
def UpdateRC(bash_completion, path_update, rc_path, bin_path): """Update the system path to include bin_path. Args: bash_completion: bool, Whether or not to do bash completion. If None, ask. path_update: bool, Whether or not to do bash completion. If None, ask. rc_path: str, The path to the rc file to update. If None, ask. bin_path: str, The absolute path to the directory that will contain Cloud SDK binaries. """ host_os = platforms.OperatingSystem.Current() if host_os == platforms.OperatingSystem.WINDOWS: if path_update is None: path_update = console_io.PromptContinue( prompt_string='Update %PATH% to include Cloud SDK binaries?') if path_update: UpdatePathForWindows(bin_path) return if not rc_path: # figure out what file to edit if host_os == platforms.OperatingSystem.LINUX: if c_gce.Metadata().connected: file_name = '.bash_profile' else: file_name = '.bashrc' elif host_os == platforms.OperatingSystem.MACOSX: file_name = '.bash_profile' elif host_os == platforms.OperatingSystem.CYGWIN: file_name = '.bashrc' elif host_os == platforms.OperatingSystem.MSYS: file_name = '.profile' else: file_name = '.bashrc' rc_path = os.path.expanduser(os.path.join('~', file_name)) rc_path_update = console_io.PromptResponse(( 'The Google Cloud SDK installer will now prompt you to update an rc ' 'file to bring the Google Cloud CLIs into your environment.\n\n' 'Enter path to an rc file to update, or leave blank to use ' '[{rc_path}]: ').format(rc_path=rc_path)) if rc_path_update: rc_path = os.path.expanduser(rc_path_update) if os.path.exists(rc_path): with open(rc_path) as rc_file: rc_data = rc_file.read() cached_rc_data = rc_data else: rc_data = '' cached_rc_data = '' updated_rc = False if path_update is None: path_update = console_io.PromptContinue( prompt_string=('\nModify profile to update your $PATH?')) path_rc_path = os.path.join(bootstrapping.SDK_ROOT, 'path.bash.inc') if path_update: path_comment = r'# The next line updates PATH for the Google Cloud SDK.' path_subre = re.compile(r'\n*' + path_comment + r'\n.*$', re.MULTILINE) path_line = "{comment}\nsource '{path_rc_path}'\n".format( comment=path_comment, path_rc_path=path_rc_path) filtered_data = path_subre.sub('', rc_data) rc_data = '{filtered_data}\n{path_line}'.format( filtered_data=filtered_data, path_line=path_line) updated_rc = True else: print("""\ Source [{path_rc_path}] in your profile to add the Google Cloud SDK command line tools to your $PATH. """.format(path_rc_path=path_rc_path)) if bash_completion is None: bash_completion = console_io.PromptContinue( prompt_string=('\nModify profile to enable bash completion?')) completion_rc_path = os.path.join(bootstrapping.SDK_ROOT, 'completion.bash.inc') if bash_completion: complete_comment = r'# The next line enables bash completion for gcloud.' complete_subre = re.compile(r'\n*' + complete_comment + r'\n.*$', re.MULTILINE) complete_line = "{comment}\nsource '{rc_path}'\n".format( comment=complete_comment, rc_path=completion_rc_path) filtered_data = complete_subre.sub('', rc_data) rc_data = '{filtered_data}\n{complete_line}'.format( filtered_data=filtered_data, complete_line=complete_line) updated_rc = True else: print("""\ Source [{completion_rc_path}] in your profile to enable bash completion for gcloud. """.format(completion_rc_path=completion_rc_path)) if not updated_rc: return if cached_rc_data == rc_data: print('No changes necessary for [{rc}].'.format(rc=rc_path)) return if os.path.exists(rc_path): rc_backup = rc_path + '.backup' print('Backing up [{rc}] to [{backup}].'.format(rc=rc_path, backup=rc_backup)) shutil.copyfile(rc_path, rc_backup) with open(rc_path, 'w') as rc_file: rc_file.write(rc_data) print("""\ [{rc_path}] has been updated. Start a new shell for the changes to take effect. """.format(rc_path=rc_path))
def Run(self, args): """Run 'deployments delete'. Args: args: argparse.Namespace, The arguments that this command was invoked with. Returns: If --async=true, returns Operation to poll. Else, returns boolean indicating whether insert operation succeeded. Raises: HttpException: An http error response was received while executing api request. ToolException: The deployment deletion operation encountered an error. """ client = self.context['deploymentmanager-v2beta2'] messages = self.context['deploymentmanager-v2beta2-messages'] project = properties.VALUES.core.project.Get(required=True) prompt_message = ('The following deployments will be deleted:\n- ' + '\n- '.join(args.deployment_name)) if not console_io.PromptContinue(message=prompt_message): raise exceptions.ToolException('Deletion aborted by user.') operations = [] for deployment_name in args.deployment_name: try: operation = client.deployments.Delete( messages.DeploymentmanagerDeploymentsDeleteRequest( project=project, deployment=deployment_name, )) except apitools_base.HttpError as error: raise exceptions.HttpException(dm_v2_util.GetError(error)) if args. async: operations.append(operation) else: op_name = operation.name try: dm_v2_util.WaitForOperation(op_name, project, self.context, 'delete', OPERATION_TIMEOUT) log.status.Print('Delete operation ' + op_name + ' completed successfully.') except (exceptions.ToolException, DeploymentManagerError): log.error('Delete operation ' + op_name + ' has errors or failed to complete within in ' + str(OPERATION_TIMEOUT) + ' seconds.') except apitools_base.HttpError as error: raise exceptions.HttpException(dm_v2_util.GetError(error)) try: completed_operation = client.operations.Get( messages.DeploymentmanagerOperationsGetRequest( project=project, operation=op_name, )) except apitools_base.HttpError as error: raise exceptions.HttpException(dm_v2_util.GetError(error)) operations.append(completed_operation) return operations
def Run(self, args): """Creates a new Cloud SQL instance. Args: args: argparse.Namespace, The arguments that this command was invoked with. Returns: A dict object representing the operations resource describing the create operation if the create was successful. Raises: HttpException: A http error response was received while executing api request. ToolException: An error other than http error occured while executing the command. """ # Added this temporarily for debugging SQL instance creation failures log.SetVerbosity(logging.DEBUG) sql_client = self.context['sql_client'] sql_messages = self.context['sql_messages'] resources = self.context['registry'] util.ValidateInstanceName(args.instance) instance_ref = resources.Parse(args.instance, collection='sql.instances') instance_resource = util.ConstructInstanceFromArgs(sql_messages, args) if args.master_instance_name: replication = 'ASYNCHRONOUS' activation_policy = 'ALWAYS' else: replication = 'SYNCHRONOUS' activation_policy = 'ON_DEMAND' if not args.replication: instance_resource.settings.replicationType = replication if not args.activation_policy: instance_resource.settings.activationPolicy = activation_policy instance_resource.project = instance_ref.project instance_resource.instance = instance_ref.instance operation_ref = None if args.pricing_plan == 'PACKAGE': if not console_io.PromptContinue( 'Charges will begin accruing immediately. Really create Cloud ' 'SQL instance?'): raise exceptions.ToolException('canceled by the user.') try: result = sql_client.instances.Insert(instance_resource) operation_ref = resources.Create( 'sql.operations', operation=result.operation, project=instance_ref.project, instance=instance_ref.instance, ) if args. async: return sql_client.operations.Get(operation_ref.Request()) util.WaitForOperation(sql_client, operation_ref, 'Creating Cloud SQL instance') log.CreatedResource(instance_ref) rsource = sql_client.instances.Get(instance_ref.Request()) cache = remote_completion.RemoteCompletion() cache.AddToCache(instance_ref.SelfLink()) return rsource except apitools_base.HttpError: log.debug('operation : %s', str(operation_ref)) raise
def UpdateRC(command_completion, path_update, rc_path, bin_path, sdk_root): """Update the system path to include bin_path. Args: command_completion: bool, Whether or not to do command completion. If None, ask. path_update: bool, Whether or not to update PATH. If None, ask. rc_path: str, The path to the rc file to update. If None, ask. bin_path: str, The absolute path to the directory that will contain Cloud SDK binaries. sdk_root: str, The path to the Cloud SDK root. """ host_os = platforms.OperatingSystem.Current() if host_os == platforms.OperatingSystem.WINDOWS: if path_update is None: path_update = console_io.PromptContinue( prompt_string='Update %PATH% to include Cloud SDK binaries?') if path_update: _UpdatePathForWindows(bin_path) return if command_completion is None: if path_update is None: # Ask only one question if both were not set. path_update = console_io.PromptContinue( prompt_string=('\nModify profile to update your $PATH ' 'and enable shell command completion?')) command_completion = path_update else: command_completion = console_io.PromptContinue( prompt_string=('\nModify profile to enable shell command ' 'completion?')) elif path_update is None: path_update = console_io.PromptContinue( prompt_string=('\nModify profile to update your $PATH?')) rc_paths = _GetRcPaths(command_completion, path_update, rc_path, sdk_root, host_os) if rc_paths.rc_path: if os.path.exists(rc_paths.rc_path): with open(rc_paths.rc_path) as rc_file: rc_data = rc_file.read() cached_rc_data = rc_data else: rc_data = '' cached_rc_data = '' if path_update: rc_data = _GetRcData( '# The next line updates PATH for the Google Cloud' ' SDK.', rc_paths.path, rc_data) if command_completion: rc_data = _GetRcData( '# The next line enables shell command completion' ' for gcloud.', rc_paths.completion, rc_data, pattern='# The next line enables [a-z][a-z]*' 'completion for gcloud.') if cached_rc_data == rc_data: print( 'No changes necessary for [{rc}].'.format(rc=rc_paths.rc_path)) return if os.path.exists(rc_paths.rc_path): rc_backup = rc_paths.rc_path + '.backup' print('Backing up [{rc}] to [{backup}].'.format( rc=rc_paths.rc_path, backup=rc_backup)) shutil.copyfile(rc_paths.rc_path, rc_backup) with open(rc_paths.rc_path, 'w') as rc_file: rc_file.write(rc_data) print("""\ [{rc_path}] has been updated. Start a new shell for the changes to take effect. """.format(rc_path=rc_paths.rc_path)) if not command_completion: print("""\ Source [{rc}] in your profile to enable shell command completion for gcloud. """.format(rc=rc_paths.completion)) if not path_update: print("""\ Source [{rc}] in your profile to add the Google Cloud SDK command line tools to your $PATH. """.format(rc=rc_paths.path))
def PromptForDeletionHelper(resource_name, prompt_list, prompt_title=None): prompt_title = (prompt_title or 'The following {0} will be deleted:'.format(resource_name)) prompt_message = ConstructList(prompt_title, prompt_list) if not console_io.PromptContinue(message=prompt_message): raise calliope_exceptions.ToolException('Deletion aborted by user.')