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