def _UpdateWithProgressBar(self, components, action, action_func): """Performs an update on a component while using a progress bar. Args: components: [schemas.Component], The components that are going to be acted on. action: str, The action that is printed for this update. action_func: func, The function to call to actually do the update. It takes a single argument which is the component id. """ for component in components: label = '{action}: {name}'.format( action=action, name=component.details.display_name) with console_io.ProgressBar(label=label, stream=log.status) as pb: action_func(component.id, progress_callback=pb.SetProgress)
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 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