예제 #1
0
    def deploy(self):
        """Prepare remote for deployment

        Sync the appstream and metadata from the remote. If the deploy
        URL differs from the pull URL, it's adjusted here, too.
        """
        self._modify_remote_for_deployment()

        # Set the flatpak remote collection ID if it's enabled and the
        # remote has a collection ID
        # Temporarily(?) disable P2P for eos-runtimes (which are legacy). See:
        # https://phabricator.endlessm.com/T22756#602959
        # https://github.com/flatpak/flatpak/issues/1832
        if self.enable_p2p_updates and self.name != 'eos-runtimes':
            repo = self.manager.get_repo()
            collection_id = fetch_remote_collection_id(repo, self.name)
            if collection_id is not None:
                self._set_collection_id(collection_id)

        # Fetch the deployed remote's appstream and metadata
        # NOTE: This is done after adding the collection ID so that the
        # ostree-metadata will be pulled as well, which enables using USB
        # updates without ever going online
        logger.info('Updating appstream data for remote %s',
                    self.name)
        eib.retry(self.installation.update_appstream_sync, self.name,
                  self.arch)
        logger.info('Updating metadata for remote %s', self.name)
        eib.retry(self.installation.update_remote_sync, self.name)

        # Reset any configuration defined metadata
        self.reset_metadata()
예제 #2
0
    def pull(self, commit_only=False, cache_repo_path=None):
        """Pull all refs to install

        Use OSTree to pull all the needed refs to a repository. If
        commit_only is True, the commit checksums are pulled without
        creating repository refs. If cache_repo_path points to an ostree
        repo, it will be used as a local object cache.
        """
        if self.install_refs is None:
            raise FlatpakError('Must run resolve_refs before pull')

        # Open the OSTree repo directly
        repo = self.get_repo()

        # Figure out common pull options
        localcache_repos = (cache_repo_path,) if cache_repo_path else ()
        common_pull_options = {
            'depth': GLib.Variant('i', 0),
            'disable-static-deltas': GLib.Variant('b', True),
            'localcache-repos': GLib.Variant('as', localcache_repos),
            'inherit-transaction': GLib.Variant('b', True),
        }

        repo.prepare_transaction()
        try:
            # Pull refs one at a time
            for ref, install_ref in sorted(self.install_refs.items()):
                remote = install_ref.full_ref.remote.name

                # Pull checksum for commit only
                if commit_only:
                    ref_to_pull = install_ref.full_ref.commit
                    logger.info('Pulling %s ref %s (commit %s)', remote,
                                ref, ref_to_pull)
                else:
                    ref_to_pull = install_ref.full_ref.ref
                    logger.info('Pulling %s ref %s', remote, ref_to_pull)

                options = common_pull_options.copy()
                options['refs'] = GLib.Variant('as', (ref_to_pull,))
                if install_ref.subpaths:
                    subdirs = self._subpaths_to_subdirs(
                        install_ref.subpaths)
                    logger.info('Pulling %s ref %s subdirs %s', remote,
                                ref, ' '.join(subdirs))
                    options.update({
                        'subdirs': GLib.Variant('as', subdirs),
                    })
                options_var = GLib.Variant('a{sv}', options)
                self._log_installation_free_space()
                eib.retry(self._do_pull, repo, remote, options_var, timeout=30)

            repo.commit_transaction()
        except:
            logger.error('Pull failed, aborting transaction')
            repo.abort_transaction()
            raise

        self.installation.drop_caches()
예제 #3
0
    def add(self):
        """Add this remote to the installation"""
        # Construct the remote
        logger.info('Adding flatpak remote %s', self.name)
        remote = Flatpak.Remote.new(self.name)
        remote.set_url(self.url)
        remote.set_gpg_verify(True)
        if self.title:
            remote.set_title(self.title)
        if self.default_branch:
            remote.set_default_branch(self.default_branch)
        if self.prio is not None:
            remote.set_prio(self.prio)

        # Import the GPG key if specified
        if self.gpg_key:
            # Strip any whitespace and decode from base64
            gpg_key_decoded = base64.b64decode(self.gpg_key.strip(),
                                               validate=True)

            # Convert to GBytes
            gpg_key_bytes = GLib.Bytes.new(gpg_key_decoded)
            remote.set_gpg_key(gpg_key_bytes)

        # Recent flatpak began automatically adding collection IDs in
        # certain scenarios, but once they're set they can't change.
        # Clear any collection ID that might be present since it may not
        # match the configured URL.
        remote.set_collection_id(None)

        # Commit the changes
        self.installation.modify_remote(remote)

        # In case the remote metadata update applies a collection ID,
        # delete any current ostree-metadata ref to ensure flatpak pulls
        # it again.
        logger.info('Deleting %s ref for %s', OSTree.REPO_METADATA_REF,
                    self.name)
        repo = self.manager.get_repo()
        repo.set_ref_immediate(self.name, OSTree.REPO_METADATA_REF, None)
        self.installation.drop_caches()

        # Fetch the deployed remote's metadata
        logger.info('Updating metadata for remote %s', self.name)
        eib.retry(self.installation.update_remote_sync, self.name)

        # Reset any configuration defined metadata
        self.reset_metadata()

        # If there's no default branch in the configuration, see if it
        # was set from the remote metadata
        if not self.default_branch:
            remote = self.installation.get_remote_by_name(self.name)
            self.default_branch = remote.get_default_branch()
            if self.default_branch:
                logger.info('Using %s as default branch for remote %s',
                            self.default_branch, self.name)
예제 #4
0
    def enumerate(self):
        """Populate refs from remote data

        Fetch the remote's data and create a FlatpakRef for each Flatpak
        app or runtime found.
        """
        logger.info('Fetching refs for %s', self.name)
        all_remote_refs = eib.retry(
            self.installation.list_remote_refs_sync, self.name)
        for remote_ref in all_remote_refs:
            # Get the full ostree ref
            ref = remote_ref.format_ref()
            logger.debug('Found %s ref %s', self.name, ref)

            # Get the installed and download size
            _, download_size, installed_size = eib.retry(
                self.installation.fetch_remote_size_sync, self.name,
                remote_ref)

            # Try to get the runtime and sdk from the flatpak metadata.
            # We could use GKeyFile as the INI parser, but ConfigParser
            # is more pleasant from python.
            #
            # We disable strict parsing so that it doesn't error on
            # duplicate sections or options, which flatpak-builder
            # apparently generates sometimes
            # (https://phabricator.endlessm.com/T22435).
            metadata_bytes = eib.retry(
                self.installation.fetch_remote_metadata_sync, self.name,
                remote_ref)
            metadata_str = metadata_bytes.get_data().decode('utf-8')
            metadata = ConfigParser(strict=False)
            try:
                metadata.read_string(metadata_str)
            except:
                print('Could not read {} {} metadata:\n{}'
                      .format(self.name, ref, metadata_str),
                      file=sys.stderr)
                raise

            # Get all the related refs
            logger.debug('Getting related refs for %s ref %s',
                         self.name, ref)
            related = eib.retry(
                self.installation.list_remote_related_refs_sync,
                self.name, ref)

            # Create FlatpakFullRef
            self.refs[ref] = FlatpakFullRef(remote=self,
                                            remote_ref=remote_ref,
                                            installed_size=installed_size,
                                            download_size=download_size,
                                            metadata=metadata,
                                            related=related)
예제 #5
0
def fetch_remote_collection_id(repo, remote):
    """Fetch the remote's collection ID from its summary file

    Fetch the remote's summary and look for the
    ostree.summary.collection-id value in the summary metadata. Return
    None if no value was found.

    Args:
        repo: An open OSTree.Repo
        remote: The OSTree remote name

    Returns:
        The collection ID string or None if one isn't set
    """
    logger.info('Fetching OSTree summary for remote %s', remote)
    _, summary_bytes, _ = eib.retry(repo.remote_fetch_summary, remote)
    summary_variant_type = GLib.VariantType.new(OSTree.SUMMARY_GVARIANT_STRING)
    summary = GLib.Variant.new_from_bytes(summary_variant_type, summary_bytes,
                                          False)

    # Look for collection ID key in the metadata. This is
    # OSTREE_SUMMARY_COLLECTION_ID in ostree, but that's not exported.
    summary_metadata = summary[1]
    collection_id = summary_metadata.get('ostree.summary.collection-id')
    if collection_id is None:
        logger.info('No collection ID in %s summary', remote)
    else:
        logger.info('Found collection ID "%s" in %s summary', collection_id,
                    remote)
    return collection_id
예제 #6
0
    def install(self):
        """Install Flatpak refs

        Find and order all Flatpak refs needed and install them with the
        installation.
        """
        if self.install_refs is None:
            raise FlatpakError('Must run resolve_refs before pull')

        # Try to order refs so dependencies are installed first. This
        # simply installs refs with no runtime dependencies first and
        # assumes flatpak won't error for any issues with extensions
        # being installed before the ref they extend.
        refs_with_runtime = []
        refs_without_runtime = []
        for _, install_ref in sorted(self.install_refs.items()):
            if install_ref.full_ref.runtime:
                refs_with_runtime.append(install_ref)
            else:
                refs_without_runtime.append(install_ref)
        refs_to_install = refs_without_runtime + refs_with_runtime

        for install_ref in refs_to_install:
            full_ref = install_ref.full_ref
            logger.info('Installing %s from %s', full_ref.ref,
                        full_ref.remote.name)
            self._log_installation_free_space()
            eib.retry(self.installation.install_full,
                      flags=Flatpak.InstallFlags.NO_STATIC_DELTAS |
                            Flatpak.InstallFlags.NO_TRIGGERS,
                      remote_name=full_ref.remote.name,
                      kind=full_ref.kind,
                      name=full_ref.name,
                      arch=full_ref.arch,
                      branch=full_ref.branch,
                      subpaths=install_ref.subpaths)

        self.installation.run_triggers()