Beispiel #1
0
def validate(snap_name, validations, revoke=False, key=None):
    """Generate, sign and upload validation assertions."""

    # Check validations format
    _check_validations(validations)

    store = storeapi.StoreClient()

    # Need the ID of the logged in user.
    with _requires_login():
        account_info = store.get_account_information()
    authority_id = account_info['account_id']

    # Get data for the gating snap
    release = storeapi.constants.DEFAULT_SERIES
    try:
        snap_id = account_info['snaps'][release][snap_name]['snap-id']
    except KeyError:
        raise storeapi.errors.SnapNotFoundError(snap_name)

    # Then, for each requested validation, generate assertion
    for validation in validations:
        gated_name, rev = validation.split('=', 1)
        approved_data = store.cpi.get_package(gated_name, 'stable')
        assertion = {
            'type': 'validation',
            'authority-id': authority_id,
            'series': release,
            'snap-id': snap_id,
            'approved-snap-id': approved_data['snap_id'],
            'approved-snap-revision': rev,
            'timestamp': datetime.datetime.utcnow().isoformat() + 'Z',
            'revoked': "false"
        }
        if revoke:
            assertion['revoked'] = "true"

        assertion = _sign_validation(validation, assertion, key)

        # Save assertion to a properly named file
        fname = '{}-{}-r{}.assertion'.format(snap_name, gated_name, rev)
        with open(fname, 'wb') as f:
            f.write(assertion)

        store.push_validation(snap_id, assertion)
Beispiel #2
0
def _push_delta(snap_name, snap_filename, source_snap):
    store = storeapi.StoreClient()
    delta_format = 'xdelta3'
    logger.info('Found cached source snap {}.'.format(source_snap))
    target_snap = os.path.join(os.getcwd(), snap_filename)

    try:
        xdelta_generator = deltas.XDelta3Generator(source_path=source_snap,
                                                   target_path=target_snap)
        delta_filename = xdelta_generator.make_delta()
    except (DeltaGenerationError, DeltaGenerationTooBigError,
            DeltaToolError) as e:
        raise storeapi.errors.StoreDeltaApplicationError(str(e))

    snap_hashes = {
        'source_hash': calculate_sha3_384(source_snap),
        'target_hash': calculate_sha3_384(target_snap),
        'delta_hash': calculate_sha3_384(delta_filename)
    }

    try:
        logger.info('Pushing delta {}.'.format(delta_filename))
        with _requires_login():
            delta_tracker = store.upload(
                snap_name,
                delta_filename,
                delta_format=delta_format,
                source_hash=snap_hashes['source_hash'],
                target_hash=snap_hashes['target_hash'],
                delta_hash=snap_hashes['delta_hash'])
        result = delta_tracker.track()
        delta_tracker.raise_for_code()
    except storeapi.errors.StoreReviewError as e:
        if e.code == 'processing_upload_delta_error':
            raise storeapi.errors.StoreDeltaApplicationError
        else:
            raise
    finally:
        if os.path.isfile(delta_filename):
            try:
                os.remove(delta_filename)
            except OSError:
                logger.warning(
                    'Unable to remove delta {}.'.format(delta_filename))
    return result
Beispiel #3
0
def _acquire_and_encrypt_credentials(packages, channels):
    """Acquire and encrypt Store credentials for Travis jobs."""
    # XXX cprov 20161116: Needs caveat syntax for restricting origins
    # (IP or reverse-dns) but Travis sudo-enabled containers, needed for
    # running xenial snapcraft, do not have static egress routes.
    # See https://docs.travis-ci.com/user/ip-addresses.
    logger.info('Acquiring specific authorization information ...')
    store = storeapi.StoreClient()
    # Travis cannot register new names or add new developers.
    acls = [
        'package_access',
        'package_push',
        'package_release',
    ]
    if not login(store=store,
                 acls=acls,
                 packages=packages,
                 channels=channels,
                 save=False):
        raise TravisRuntimeError(
            'Cannot continue without logging in successfully.')

    logger.info('Encrypting authorization for Travis and adjusting project to '
                'automatically decrypt and use it during "after_success".')
    with tempfile.NamedTemporaryFile(mode='w') as fd:
        store.conf.save(config_fd=fd)
        fd.flush()
        os.makedirs(os.path.dirname(LOCAL_CONFIG_FILENAME), exist_ok=True)
        cmd = [
            'travis',
            'encrypt-file',
            '--force',
            '--add',
            'after_success',
            '--decrypt-to',
            LOCAL_CONFIG_FILENAME,
            fd.name,
            ENCRYPTED_CONFIG_FILENAME,
        ]
        try:
            subprocess.check_output(cmd, stderr=subprocess.PIPE)
        except subprocess.CalledProcessError as err:
            raise TravisRuntimeError(
                '`travis encrypt-file` failed: {}\n{}'.format(
                    err.returncode, err.stderr.decode()))
Beispiel #4
0
def login(*,
          store: storeapi.StoreClient = None,
          packages: Iterable[Dict[str, str]] = None,
          save: bool = True,
          acls: Iterable[str] = None,
          channels: Iterable[str] = None,
          expires: str = None,
          config_fd: TextIO = None) -> bool:
    if not store:
        store = storeapi.StoreClient()

    email = ""
    password = ""

    if not config_fd:
        print("Enter your Ubuntu One e-mail address and password.\n"
              "If you do not have an Ubuntu One account, you can create one "
              "at https://dashboard.snapcraft.io/openid/login")
        email = input("Email: ")
        if os.environ.get("SNAPCRAFT_TEST_INPUT"):
            password = input("Password: "******"Password: ")

    try:
        _try_login(
            email,
            password,
            store=store,
            packages=packages,
            acls=acls,
            channels=channels,
            expires=expires,
            config_fd=config_fd,
            save=save,
        )
    # Let StoreAuthenticationError pass through so we get decent error messages
    except storeapi.errors.InvalidCredentialsError:
        return _fail_login(storeapi.constants.INVALID_CREDENTIALS)
    except storeapi.errors.StoreAccountInformationError:
        return _fail_login(storeapi.constants.ACCOUNT_INFORMATION_ERROR)
    except storeapi.errors.NeedTermsSignedError as e:
        return _fail_login(e.message)  # type: ignore

    return True
Beispiel #5
0
def validate(snap_name, validations, revoke=False, key=None):
    """Generate, sign and upload validation assertions."""
    # Check validations format
    _check_validations(validations)

    store = storeapi.StoreClient()

    # Need the ID of the logged in user.
    with _requires_login():
        account_info = store.get_account_information()
    authority_id = account_info["account_id"]

    # Get data for the gating snap
    release = storeapi.constants.DEFAULT_SERIES
    try:
        snap_id = account_info["snaps"][release][snap_name]["snap-id"]
    except KeyError:
        raise storeapi.errors.SnapNotFoundError(snap_name)

    # Then, for each requested validation, generate assertion
    for validation in validations:
        gated_name, rev = validation.split("=", 1)
        echo.info("Getting details for {}".format(gated_name))
        approved_data = store.cpi.get_package(gated_name, "stable")
        assertion = {
            "type": "validation",
            "authority-id": authority_id,
            "series": release,
            "snap-id": snap_id,
            "approved-snap-id": approved_data["snap_id"],
            "approved-snap-revision": rev,
            "timestamp": datetime.utcnow().isoformat() + "Z",
            "revoked": "false",
        }
        if revoke:
            assertion["revoked"] = "true"

        assertion = _sign_assertion(validation, assertion, key, "validations")

        # Save assertion to a properly named file
        fname = "{}-{}-r{}.assertion".format(snap_name, gated_name, rev)
        with open(fname, "wb") as f:
            f.write(assertion)

        store.push_assertion(snap_id, assertion, endpoint="validations")
Beispiel #6
0
def release(snap_name, revision, release_channels):
    store = storeapi.StoreClient()
    with _requires_login():
        channels = store.release(snap_name, revision, release_channels)

    if 'opened_channels' in channels:
        logger.info(_get_text_for_opened_channels(channels['opened_channels']))
        # There should be an empty line between the open channels
        # message and what follows
        print()
    channel_map = channels['channel_map']
    parsed_channels = [_get_text_for_channel(c) for c in channel_map]
    tabulated_channels = tabulate(parsed_channels,
                                  numalign='left',
                                  headers=['Channel', 'Version', 'Revision'],
                                  tablefmt='plain')
    # This does not look good in green so we print instead
    print(tabulated_channels)
Beispiel #7
0
def revisions(snap_name, series, arch):
    store = storeapi.StoreClient()

    with _requires_login():
        revisions = store.get_snap_revisions(snap_name, series, arch)

    parsed_revisions = [
        (rev['revision'], rev['timestamp'], rev['arch'], rev['version'],
         _get_text_for_current_channels(rev['channels'],
                                        rev['current_channels']))
        for rev in revisions
    ]
    tabulated_revisions = tabulate(
        parsed_revisions,
        numalign='left',
        headers=['Rev.', 'Uploaded', 'Arch', 'Version', 'Channels'],
        tablefmt='plain')
    print(tabulated_revisions)
Beispiel #8
0
def download(snap_name, channel, download_path, arch):
    """Download snap from the store to download_path"""
    try:
        store = storeapi.StoreClient()
        store.download(snap_name, channel, download_path, arch)
    except storeapi.errors.InvalidCredentialsError:
        logger.error('No valid credentials found.'
                     ' Have you run "snapcraft login"?')
        raise
    except storeapi.errors.SnapNotFoundError:
        raise RuntimeError('Snap {name} for {arch} cannot be found'
                           ' in the {channel} channel'.format(name=snap_name,
                                                              arch=arch,
                                                              channel=channel))
    except storeapi.errors.SHAMismatchError:
        raise RuntimeError(
            'Failed to download {} at {} (mismatched SHA)'.format(
                snap_name, download_path))
Beispiel #9
0
def close(snap_name, channels):
    """Close <channel> for <snap-name>.
    Closing a channel allows the <channel> that is closed to track the channel
    that follows it in the channel release chain. As such closing the
    'candidate' channel would make it track the 'stable' channel.

    The channel map will be displayed after the operation takes place.

    \b
    Examples:
        snapcraft close my-snap beta
        snapcraft close my-snap beta edge
    """
    store = storeapi.StoreClient()
    account_info = store.get_account_information()

    try:
        snap_id = account_info["snaps"][DEFAULT_SERIES][snap_name]["snap-id"]
    except KeyError:
        raise storeapi.errors.StoreChannelClosingPermissionError(
            snap_name, DEFAULT_SERIES)

    # Returned closed_channels cannot be trusted as it returns risks.
    store.close_channels(snap_id=snap_id, channel_names=channels)
    if len(channels) == 1:
        msg = "The {} channel is now closed.".format(channels[0])
    else:
        msg = "The {} and {} channels are now closed.".format(
            ", ".join(channels[:-1]), channels[-1])

    snap_channel_map = store.get_snap_channel_map(snap_name=snap_name)
    if snap_channel_map.channel_map:
        closed_tracks = {storeapi.channels.Channel(c).track for c in channels}
        existing_architectures = snap_channel_map.get_existing_architectures()

        click.echo(
            get_tabulated_channel_map(
                snap_channel_map,
                architectures=existing_architectures,
                tracks=closed_tracks,
            ))
        click.echo()

    echo.info(msg)
Beispiel #10
0
def login(
    *,
    store: storeapi.StoreClient,
    packages: Iterable[Dict[str, str]] = None,
    save: bool = True,
    acls: Iterable[str] = None,
    channels: Iterable[str] = None,
    expires: str = None,
    config_fd: TextIO = None,
) -> bool:
    if not store:
        store = storeapi.StoreClient()

    email = ""
    password = ""

    if not config_fd:
        echo.wrapped("Enter your Ubuntu One e-mail address and password.")
        echo.wrapped(
            "If you do not have an Ubuntu One account, you can create one "
            "at https://snapcraft.io/account"
        )
        email = echo.prompt("Email")
        if os.getenv("SNAPCRAFT_TEST_INPUT"):
            # Integration tests do not work well with hidden input.
            echo.warning("Password will be visible.")
            hide_input = False
        else:
            hide_input = True
        password = echo.prompt("Password", hide_input=hide_input)

    _try_login(
        email,
        password,
        store=store,
        packages=packages,
        acls=acls,
        channels=channels,
        expires=expires,
        config_fd=config_fd,
        save=save,
    )

    return True
Beispiel #11
0
def collaborate(snap_name, key):
    store = storeapi.StoreClient()

    with _requires_login():
        account_info = store.get_account_information()
    publisher_id = account_info['account_id']

    release = storeapi.constants.DEFAULT_SERIES
    try:
        snap_id = account_info['snaps'][release][snap_name]['snap-id']
    except KeyError:
        raise storeapi.errors.SnapNotFoundError(snap_name)
    assertion = _get_developers(snap_id, publisher_id)
    # The data will look like:
    # {'snap_developer': {
    #      'type': 'snap-developer',
    #      'authority-id': <account_id of the publisher>,
    #      'publisher-id': <account_id of the publisher>,
    #      'snap-id': 'snap_id',
    #      'developers': [{
    #          'developer-id': 'account_id of dev-1',
    #          'since': '2017-02-10T08:35:00.390258Z'
    #         },{
    #          'developer-id': 'account_id of dev-2',
    #          'since': '2017-02-10T08:35:00.390258Z',
    #          'until': '2018-02-10T08:35:00.390258Z'
    #         }],
    #      }
    # }
    developers = _edit_collaborators(assertion.get('developers', []))
    if _are_developers_unchanged(developers, assertion.get('developers', [])):
        logger.warning('Aborting due to unchanged collaborators list.')
        return
    assertion['developers'] = developers

    # The revision should be incremented, to avoid `invalid-revision` errors.
    assertion['revision'] = str(int(assertion.get('revision', '0')) + 1)

    # There is a possibility that the `authority-id` to be `canonical`,
    # which should be changed to the `publisher_id` to match the signing key.
    assertion['authority-id'] = publisher_id

    signed_assertion = _sign_assertion(snap_name, assertion, key, 'developers')
    store.push_assertion(snap_id, signed_assertion, 'developers')
Beispiel #12
0
def register_key(name):
    if not repo.Repo.is_package_installed("snapd"):
        raise storeapi.errors.MissingSnapdError("register-key")
    key = _maybe_prompt_for_key(name)
    store = storeapi.StoreClient()
    try:
        if not login(store=store, acls=["modify_account_key"], save=False):
            raise storeapi.errors.LoginRequiredError()
    except storeapi.errors.StoreAuthenticationError as e:
        raise storeapi.errors.LoginRequiredError(str(e)) from e
    logger.info("Registering key ...")
    account_info = store.get_account_information()
    account_key_request = _export_key(key["name"], account_info["account_id"])
    store.register_key(account_key_request)
    logger.info(
        'Done. The key "{}" ({}) may be used to sign your assertions.'.format(
            key["name"], key["sha3-384"]
        )
    )
Beispiel #13
0
def list_keys():
    if not repo.Repo.is_package_installed('snapd'):
        raise storeapi.errors.MissingSnapdError('list-keys')
    keys = list(_get_usable_keys())
    store = storeapi.StoreClient()
    with _requires_login():
        account_info = store.get_account_information()
    enabled_keys = {
        account_key['public-key-sha3-384']
        for account_key in account_info['account_keys']
    }
    tabulated_keys = tabulate(
        [('*' if key['sha3-384'] in enabled_keys else '-', key['name'],
          key['sha3-384'],
          '' if key['sha3-384'] in enabled_keys else '(not registered)')
         for key in keys],
        headers=["", "Name", "SHA3-384 fingerprint", ""],
        tablefmt="plain")
    print(tabulated_keys)
Beispiel #14
0
def login():
    print('Enter your Ubuntu One SSO credentials.')
    email = input('Email: ')
    password = getpass.getpass('Password: '******'One-time password (just press enter if you don\'t use two-factor '
        'authentication): ')

    logger.info('Authenticating against Ubuntu One SSO.')
    store = storeapi.StoreClient()
    response = store.login(email,
                           password,
                           one_time_password=one_time_password)
    success = response.get('success', False)

    if success:
        logger.info('Login successful.')
    else:
        logger.info('Login failed.')
    return success
Beispiel #15
0
def login():
    print('Enter your Ubuntu One SSO credentials.')
    email = input('Email: ')
    password = getpass.getpass('Password: '******'One-time password (just press enter if you don\'t use two-factor '
        'authentication): ')

    logger.info('Authenticating against Ubuntu One SSO.')
    store = storeapi.StoreClient()
    try:
        store.login(
            email, password, one_time_password=one_time_password)
    except (storeapi.errors.InvalidCredentialsError,
            storeapi.errors.StoreAuthenticationError):
        logger.info('Login failed.')
        return False
    else:
        logger.info('Login successful.')
        return True
Beispiel #16
0
def list_keys():
    if not repo.Repo.is_package_installed('snapd'):
        raise EnvironmentError(
            'The snapd package is not installed. In order to use `list-keys`, '
            'you must run `apt install snapd`.')
    keys = list(_get_usable_keys())
    store = storeapi.StoreClient()
    with _requires_login():
        account_info = store.get_account_information()
    enabled_keys = {
        account_key['public-key-sha3-384']
        for account_key in account_info['account_keys']}
    tabulated_keys = tabulate(
        [('*' if key['sha3-384'] in enabled_keys else '-',
          key['name'], key['sha3-384'],
          '' if key['sha3-384'] in enabled_keys else '(not registered)')
         for key in keys],
        headers=["", "Name", "SHA3-384 fingerprint", ""],
        tablefmt="plain")
    print(tabulated_keys)
Beispiel #17
0
def revisions(snap_name, series, arch):
    store = storeapi.StoreClient()

    with _requires_login():
        revisions = store.get_snap_revisions(snap_name, series, arch)

    parsed_revisions = [(
        rev["revision"],
        rev["timestamp"],
        rev["arch"],
        rev["version"],
        _get_text_for_current_channels(rev["channels"],
                                       rev["current_channels"]),
    ) for rev in revisions]
    tabulated_revisions = tabulate(
        parsed_revisions,
        numalign="left",
        headers=["Rev.", "Uploaded", "Arch", "Version", "Channels"],
        tablefmt="plain",
    )
    print(tabulated_revisions)
Beispiel #18
0
def release(snap_name, revision, release_channels):
    try:
        store = storeapi.StoreClient()
        channels = store.release(snap_name, revision, release_channels)
    except storeapi.errors.InvalidCredentialsError:
        logger.error('No valid credentials found.'
                     ' Have you run "snapcraft login"?')
        raise

    if 'opened_channels' in channels:
        logger.info(
            _get_text_for_opened_channels(channels['opened_channels']))
        # There should be an empty line between the open channels
        # message and what follows
        print()
    channel_map = channels['channel_map']
    parsed_channels = [_get_text_for_channel(c) for c in channel_map]
    tabulated_channels = tabulate(parsed_channels,
                                  headers=['Channel', 'Version', 'Revision'])
    # This does not look good in green so we print instead
    print(tabulated_channels)
Beispiel #19
0
 def setUp(self):
     super().setUp()
     self.fake_store = self.useFixture(fixture_setup.FakeStore())
     self.client = storeapi.StoreClient()
     self.expected = [{
         'series': ['16'],
         'channels': [],
         'version': '2.0.1',
         'timestamp': '2016-09-27T19:23:40Z',
         'current_channels': ['beta', 'edge'],
         'arch': 'i386',
         'revision': 2
     }, {
         'series': ['16'],
         'channels': ['stable', 'edge'],
         'version': '2.0.2',
         'timestamp': '2016-09-27T18:38:43Z',
         'current_channels': ['stable', 'candidate', 'beta'],
         'arch': 'amd64',
         'revision': 1,
     }]
Beispiel #20
0
def download(snap_name, channel, download_path, arch, except_hash=''):
    """Download snap from the store to download_path.
    :param str snap_name: The snap name to download.
    :param str channel: the channel to get the snap from.
    :param str download_path: the path to write the downloaded snap to.
    :param str arch: the architecture of the download as a deb arch.
    :param str except_hash: do not download if set to a sha3_384 hash that
                            matches the snap_name to be downloaded.
    :raises storeapi.errors.SHAMismatchErrorRuntimeError:
         If the checksum for the downloaded file does not match the expected
         hash.
    :returns: A sha3_384 of the file that was or would have been downloaded.
    """
    store = storeapi.StoreClient()
    try:
        return store.download(snap_name, channel, download_path, arch,
                              except_hash)
    except storeapi.errors.SHAMismatchError:
        raise RuntimeError(
            'Failed to download {} at {} (mismatched SHA)'.format(
                snap_name, download_path))
Beispiel #21
0
 def setUp(self):
     super().setUp()
     self.fake_store = self.useFixture(fixture_setup.FakeStore())
     self.client = storeapi.StoreClient()
     self.expected = {
         'i386': [
             {
                 'info': 'none',
                 'channel': 'stable'
             },
             {
                 'info': 'none',
                 'channel': 'beta'
             },
             {
                 'info': 'specific',
                 'version': '1.0-i386',
                 'channel': 'edge',
                 'revision': 3
             },
         ],
         'amd64': [
             {
                 'info': 'specific',
                 'version': '1.0-amd64',
                 'channel': 'stable',
                 'revision': 2
             },
             {
                 'info': 'specific',
                 'version': '1.1-amd64',
                 'channel': 'beta',
                 'revision': 4
             },
             {
                 'info': 'tracking',
                 'channel': 'edge'
             },
         ],
     }
Beispiel #22
0
def login(*,
          store: storeapi.StoreClient = None,
          packages: Iterable[Dict[str, str]] = None,
          save: bool = True,
          acls: Iterable[str] = None,
          channels: Iterable[str] = None,
          config_fd: TextIO = None) -> bool:
    if not store:
        store = storeapi.StoreClient()

    email = ''
    password = ''

    if not config_fd:
        print('Enter your Ubuntu One e-mail address and password.\n'
              'If you do not have an Ubuntu One account, you can create one '
              'at https://dashboard.snapcraft.io/openid/login')
        email = input('Email: ')
        password = getpass.getpass('Password: ')

    try:
        _try_login(email,
                   password,
                   store=store,
                   packages=packages,
                   acls=acls,
                   channels=channels,
                   config_fd=config_fd,
                   save=save)
    except storeapi.errors.InvalidCredentialsError:
        return _fail_login(storeapi.constants.INVALID_CREDENTIALS)
    except storeapi.errors.StoreAuthenticationError:
        return _fail_login(storeapi.constants.AUTHENTICATION_ERROR)
    except storeapi.errors.StoreAccountInformationError:
        return _fail_login(storeapi.constants.ACCOUNT_INFORMATION_ERROR)
    except storeapi.errors.NeedTermsSignedError as e:
        return _fail_login(e.message)  # type: ignore
    else:
        return True
Beispiel #23
0
    def _set_data(self) -> None:
        op = self.get_op()
        host_snap_repo = self._get_snap_repo()

        install_cmd = []  # type: List[str]
        snap_revision = None

        if op == _SnapOp.INJECT:
            install_cmd = ["snap", "install"]
            host_snap_info = host_snap_repo.get_local_snap_info()
            snap_revision = host_snap_info["revision"]

            if snap_revision.startswith("x"):
                install_cmd.append("--dangerous")

            if host_snap_info["confinement"] == "classic":
                install_cmd.append("--classic")

            snap_file_name = "{}_{}.snap".format(host_snap_repo.name,
                                                 snap_revision)
            install_cmd.append(os.path.join(self._snap_dir, snap_file_name))
        elif op == _SnapOp.INSTALL or op == _SnapOp.REFRESH:
            install_cmd = ["snap", op.name.lower()]
            snap_channel = _get_snap_channel(self.snap_name)
            store_snap_info = storeapi.StoreClient().cpi.get_info(
                self.snap_name)
            snap_channel_map = store_snap_info.get_channel_mapping(
                risk=snap_channel.risk,
                track=snap_channel.track,
                arch=self._snap_arch)
            snap_revision = snap_channel_map.revision
            if snap_channel_map.confinement == "classic":
                install_cmd.append("--classic")
            install_cmd.extend(
                ["--channel", snap_channel_map.channel_details.name])
            install_cmd.append(host_snap_repo.name)

        self.__install_cmd = install_cmd
        self.__revision = snap_revision
Beispiel #24
0
def gated(snap_name):
    """Print list of snaps gated by snap_name."""
    store = storeapi.StoreClient()
    # Get data for the gating snap
    with _requires_login():
        snaps = store.get_account_information().get("snaps", {})

    release = storeapi.constants.DEFAULT_SERIES
    # Resolve name to snap-id
    try:
        snap_id = snaps[release][snap_name]["snap-id"]
    except KeyError:
        raise storeapi.errors.SnapNotFoundError(snap_name)

    validations = store.get_assertion(snap_id, endpoint="validations")

    if validations:
        table_data = []
        for v in validations:
            name = v["approved-snap-name"]
            revision = v["approved-snap-revision"]
            if revision == "-":
                revision = None
            required = str(v.get("required", True))
            # Currently timestamps have microseconds, which look bad
            timestamp = v["timestamp"]
            if "." in timestamp:
                timestamp = timestamp.split(".")[0] + "Z"
            table_data.append([name, revision, required, timestamp])
        tabulated = tabulate(
            table_data,
            headers=["Name", "Revision", "Required", "Approved"],
            tablefmt="plain",
            missingval="-",
        )
        print(tabulated)
    else:
        print("There are no validations for snap {!r}".format(snap_name))
Beispiel #25
0
def login(login_file, experimental_login: bool):
    """Login with your Ubuntu One e-mail address and password.

    If you do not have an Ubuntu One account, you can create one at
    https://snapcraft.io/account
    """
    store_client = storeapi.StoreClient(use_candid=experimental_login)
    if store_client.use_candid:
        store_client.login(config_fd=login_file, save=True)
    else:
        snapcraft.login(store=store_client, config_fd=login_file)

    print()

    if login_file:
        try:
            human_acls = _human_readable_acls(store_client)
            echo.info("Login successful. You now have these capabilities:\n")
            echo.info(human_acls)
        except NotImplementedError:
            echo.info("Login successful.")
    else:
        echo.info("Login successful.")
Beispiel #26
0
def register_key(name):
    if not repo.is_package_installed('snapd'):
        raise EnvironmentError(
            'The snapd package is not installed. In order to use '
            '`register-key`, you must run `apt install snapd`.')
    keys = list(_get_usable_keys(name=name))
    if not keys:
        if name is not None:
            raise RuntimeError(
                'You have no usable key named "{}".'.format(name))
        else:
            raise RuntimeError('You have no usable keys.')
    key = _select_key(keys)
    store = storeapi.StoreClient()
    if not _login(store, acls=['modify_account_key'], save=False):
        raise RuntimeError('Cannot continue without logging in successfully.')
    logger.info('Registering key ...')
    account_info = store.get_account_information()
    account_key_request = _export_key(key['name'], account_info['account_id'])
    store.register_key(account_key_request)
    logger.info(
        'Done. The key "{}" ({}) may be used to sign your assertions.'.format(
            key['name'], key['sha3-384']))
Beispiel #27
0
def upload(snap_filename):
    logger.info('Uploading existing {}.'.format(snap_filename))

    try:
        store = storeapi.StoreClient()
        result = store.upload(snap_filename)
    except storeapi.errors.InvalidCredentialsError:
        logger.error('No valid credentials found.'
                     ' Have you run "snapcraft login"?')
        raise

    success = result.get('success', False)
    errors = result.get('errors', [])
    app_url = result.get('application_url', '')
    revision = result.get('revision')

    # Print another newline to make sure the user sees the final result of the
    # upload (success/failure).
    print()

    if success:
        message = 'Application uploaded successfully'
        if revision:
            message = '{} (as revision {})'.format(message, revision)
        logger.info(message)
    else:
        logger.info('Upload did not complete.')

    if errors:
        logger.info('Some errors were detected:\n\n{}\n'.format('\n'.join(
            str(error) for error in errors)))

    if app_url:
        logger.info(
            'Please check out the application at: {}\n'.format(app_url))

    return success
Beispiel #28
0
def gated(snap_name):
    """Print list of snaps gated by snap_name."""
    store = storeapi.StoreClient()
    # Get data for the gating snap
    with _requires_login():
        snaps = store.get_account_information().get('snaps', {})

    release = storeapi.constants.DEFAULT_SERIES
    # Resolve name to snap-id
    try:
        snap_id = snaps[release][snap_name]['snap-id']
    except KeyError:
        raise storeapi.errors.SnapNotFoundError(snap_name)

    validations = store.get_assertion(snap_id, endpoint='validations')

    if validations:
        table_data = []
        for v in validations:
            name = v['approved-snap-name']
            revision = v['approved-snap-revision']
            if revision == '-':
                revision = None
            required = str(v.get('required', True))
            # Currently timestamps have microseconds, which look bad
            timestamp = v['timestamp']
            if '.' in timestamp:
                timestamp = timestamp.split('.')[0] + 'Z'
            table_data.append([name, revision, required, timestamp])
        tabulated = tabulate(
            table_data,
            headers=['Name', 'Revision', 'Required', 'Approved'],
            tablefmt="plain",
            missingval='-')
        print(tabulated)
    else:
        print('There are no validations for snap {!r}'.format(snap_name))
Beispiel #29
0
    def _set_data(self) -> None:
        op = self.get_op()
        host_snap_repo = self._get_snap_repo()
        install_cmd = ["sudo", "snap"]

        if op == _SnapOp.INJECT:
            install_cmd.append("install")
            host_snap_info = host_snap_repo.get_local_snap_info()
            snap_revision = host_snap_info["revision"]

            if snap_revision.startswith("x"):
                install_cmd.append("--dangerous")

            if host_snap_info["confinement"] == "classic":
                install_cmd.append("--classic")

            snap_file_name = "{}_{}.snap".format(host_snap_repo.name, snap_revision)
            install_cmd.append(os.path.join(self._snap_dir, snap_file_name))
        elif op == _SnapOp.INSTALL or op == _SnapOp.REFRESH:
            install_cmd.append(op.name.lower())
            snap_channel = _get_snap_channel(self.snap_name)
            store_snap_info = storeapi.StoreClient().cpi.get_package(
                self.snap_name, snap_channel, self._snap_arch
            )
            snap_revision = store_snap_info["revision"]
            confinement = store_snap_info["confinement"]
            if confinement == "classic":
                install_cmd.append("--classic")
            install_cmd.extend(["--channel", snap_channel])
            install_cmd.append(host_snap_repo.name)
        elif op == _SnapOp.NOP:
            install_cmd = []
            snap_revision = None

        self.__install_cmd = install_cmd
        self.__revision = snap_revision
Beispiel #30
0
def list_registered():
    series = storeapi.constants.DEFAULT_SERIES

    store = storeapi.StoreClient()
    with _requires_login():
        account_info = store.get_account_information()
    snaps = [
        (name, info['since'], 'private' if info['private'] else 'public',
         info['price'] or '-', '-')
        for name, info in account_info['snaps'].get(series, {}).items()
        # Presenting only approved snap registrations, which means name
        # disputes will be displayed/sorted some other way.
        if info['status'] == 'Approved'
    ]

    if not snaps:
        print('There are no registered snaps for series {!r}.'.format(series))
        return

    tabulated_snaps = tabulate(
        sorted(snaps, key=operator.itemgetter(0)),
        headers=['Name', 'Since', 'Visibility', 'Price', 'Notes'],
        tablefmt='plain')
    print(tabulated_snaps)